From 42a3084a6f6dadc7097906b76d6d9cc7b4523c94 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Wed, 31 Jan 2024 17:58:24 +0800 Subject: [PATCH 01/74] Update environment.md fix environment mongo username (#1847) --- docs/contrib/environment.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contrib/environment.md b/docs/contrib/environment.md index fb696eb54..e18246afa 100644 --- a/docs/contrib/environment.md +++ b/docs/contrib/environment.md @@ -311,7 +311,7 @@ This section involves setting up MongoDB, including its port, address, and crede | MONGO_ADDRESS | [Generated IP] | IP address for MongoDB. | | MONGO_USERNAME | [User Defined] | Admin Username for MongoDB. | | MONGO_PASSWORD | [User Defined] | Admin Password for MongoDB. | -| MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Username for MongoDB. | +| MONGO_OPENIM_USERNAME | [User Defined] | OpenIM Username for MongoDB. | | MONGO_OPENIM_PASSWORD | [User Defined] | OpenIM Password for MongoDB. | ### 2.8. Tencent Cloud COS Configuration From 4c0121849d8240c471a1eb2a18e56d046ff506dd Mon Sep 17 00:00:00 2001 From: Seal Bell <495950007@qq.com> Date: Thu, 1 Feb 2024 09:38:21 +0800 Subject: [PATCH 02/74] docs:Add multi-language README.md in ./docs/ file (#1852) * Add Simplified Chinese README.md * Add Ukrainian README.md * Add Traditional Chinese README.md * Add Czech README.md * Add Hungarian README.md * Add spanish README.md * Add Persian README.md * Add French README.md * Add German README.md * Add Japanese README.md * Add Polish README.md * Add Indonesian README.md * Add Finnish README.md * Add Malayalam README.md * Add Dutch README.md * Add Italian README.md * Add Russian README.md * Added Brazilian Portuguese README.md * Add Esperanto README.md * Add Korean README.md * Add Vietnamese README.md * Add Arabic README.md * Add Danish README.md * Add Greek README.md * Add Turkish README.md --- docs/README_ar.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_cs.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_da.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_de.md | 66 +++++++++++++++++++++++++++++++++++++++++++ docs/README_el.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_eo.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_es.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_fa.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_fi.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_fr.md | 66 +++++++++++++++++++++++++++++++++++++++++++ docs/README_hu.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_id.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_it.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_ja.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_ko.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_ml.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_nl.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_pl.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_pt_BR.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_ru.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_tr.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_ua.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_vi.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_zh_CN.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ docs/README_zh_TW.md | 67 ++++++++++++++++++++++++++++++++++++++++++++ 25 files changed, 1673 insertions(+) create mode 100644 docs/README_ar.md create mode 100644 docs/README_cs.md create mode 100644 docs/README_da.md create mode 100644 docs/README_de.md create mode 100644 docs/README_el.md create mode 100644 docs/README_eo.md create mode 100644 docs/README_es.md create mode 100644 docs/README_fa.md create mode 100644 docs/README_fi.md create mode 100644 docs/README_fr.md create mode 100644 docs/README_hu.md create mode 100644 docs/README_id.md create mode 100644 docs/README_it.md create mode 100644 docs/README_ja.md create mode 100644 docs/README_ko.md create mode 100644 docs/README_ml.md create mode 100644 docs/README_nl.md create mode 100644 docs/README_pl.md create mode 100644 docs/README_pt_BR.md create mode 100644 docs/README_ru.md create mode 100644 docs/README_tr.md create mode 100644 docs/README_ua.md create mode 100644 docs/README_vi.md create mode 100644 docs/README_zh_CN.md create mode 100644 docs/README_zh_TW.md diff --git a/docs/README_ar.md b/docs/README_ar.md new file mode 100644 index 000000000..bdd616e6a --- /dev/null +++ b/docs/README_ar.md @@ -0,0 +1,67 @@ +# وثائق OpenIM Server + +مرحبًا بكم في مركز وثائق OpenIM! يوفر هذا المركز مجموعة شاملة من الأدلة والكتيبات التي صُممت لمساعدتك في الاستفادة القصوى من تجربة OpenIM الخاصة بك. + +## جدول المحتويات + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - إرشادات حول المساهمة والتكوينات للمطورين +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - اتفاقيات الكود، سياسات التسجيل، وأدوات التحويل الأخرى + +------ + +## مساهمة + +هذا القسم يقدم للمطورين دليلاً مفصلاً حول كيفية المساهمة في الكود، إعداد بيئتهم، واتباع العمليات المرتبطة. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - قواعد واتفاقيات لكتابة الكود في OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - دليل حول كيفية القيام بالتطوير داخل OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - إرشادات حول عمليات اختيار الجيت. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - سير عمل الجيت في OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - إرشادات حول إعداد وتهيئة OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - كيفية تثبيت الدوكر على جهازك. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - دليل لإعداد بيئة التطوير على لينكس. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - إرشادات حول كيفية القيام ببعض الأعمال الشائعة محليًا. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - طرق توظيف OpenIM دون اتصال. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - دليل حول استخدام أدوات بروتوك. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - أدوات ومكتبات في OpenIM للغة Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - أفضل الممارسات والأدوات لملفات الصيانة. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - أفضل الممارسات والأدوات للسكربتات. + +## التحويلات + +يقدم هذا القسم مختلف الاتفاقيات والسياسات داخل OpenIM، التي تشمل الكود، السجلات، الإصدارات، والمزيد. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - إرشادات وطرق لتحويلات API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - سياسات واتفاقيات التسجيل في OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - إجراءات واتفاقيات لـ CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - اتفاقيات لالتزامات الكود في OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - هيكل الدليل واتفاقياته داخل OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - قائمة وأوصاف رموز الخطأ. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - اتفاقيات وتحويلات لكود Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - استراتيجيات إدارة صور الدوكر في OpenIM، تشمل عدة معماريات ومستودعات الصور. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - اتفاقيات أكثر تفصيلاً حول التسجيل. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - استراتيجيات التسمية والإدارة لإصدارات OpenIM. + + +## للمطورين والمساهمين ومشرفي المجتمع + +### المطورون والمساهمون + +إذا كنت مطورًا أو شخصًا حريصًا على المساهمة: + +- تعرف على [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) و[Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) لضمان سلاسة المساهمات. +- اغمر نفسك في [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) للتعرف على ممارسات التطوير في OpenIM. + +### مشرفو المجتمع + +كمشرف على المجتمع: + +- تأكد من أن المساهمات تتوافق مع المعايير الموضحة في وثائقنا. +- راجع بانتظام [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) و[Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) للبقاء على اطلاع. + +## للمستخدمين + +يجب أن يولي المستخدمون اهتمامًا خاصًا لـ: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - ضروري إذا كنت تخطط لاستخدام صور الدوكر لـ OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - لفهم الصور المختلفة المتاحة وكيفية اختيار ال diff --git a/docs/README_cs.md b/docs/README_cs.md new file mode 100644 index 000000000..0c40b63b1 --- /dev/null +++ b/docs/README_cs.md @@ -0,0 +1,67 @@ +# Dokumenty serveru OpenIM + +Vítejte v centru dokumentace OpenIM! Toto centrum poskytuje komplexní řadu průvodců a manuálů navržených tak, aby vám pomohly co nejlépe využít vaše zkušenosti s OpenIM. + +## Obsah + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Pokyny pro přispívání a konfigurace pro vývojáře +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konvence kódování, zásady protokolování a další transformační nástroje + +------ + +## Contrib + +Tato část nabízí vývojářům podrobný návod, jak přispívat kódem, nastavovat prostředí a sledovat související procesy. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Pravidla a konvence pro psaní kódu v OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Návod, jak provádět vývoj v rámci OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Pokyny pro operace sběru třešní. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Pracovní postup git v OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Pokyny k nastavení a inicializaci OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Jak nainstalovat Docker na váš počítač. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Průvodce nastavením vývojového prostředí na Linuxu. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Pokyny k provádění určitých společných akcí na místní úrovni. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metody nasazení OpenIM offline. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Průvodce používáním protokolových nástrojů. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Nástroje a knihovny v OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Osvědčené postupy a nástroje pro Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Doporučené postupy a nástroje pro skripty. + +## Konverze + +Tato část představuje různé konvence a zásady v rámci OpenIM, zahrnující kód, protokoly, verze a další. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Pokyny a metody pro konverze API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Zásady a konvence protokolování v OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Postupy a konvence pro CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvence pro odevzdání kódu v OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktura adresářů a konvence v rámci OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Seznam a popisy chybových kódů. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvence a převody pro kód Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie správy pro obrazy OpenIM Docker, zahrnující různé architektury a úložiště obrazů. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Další podrobné konvence o protokolování. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie pojmenování a správy verzí OpenIM. + + +## Pro vývojáře, přispěvatele a správce komunity + +### Vývojáři a přispěvatelé + +Pokud jste vývojář nebo někdo, kdo má zájem přispívat: + +- Seznamte se s našimi [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) a [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), abyste zajistili hladké příspěvky. +- Ponořte se do [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), abyste se seznámili s vývojovými postupy v OpenIM. + +### Správci komunity + +Jako správce komunity: + +- Zajistěte, aby příspěvky odpovídaly standardům uvedeným v naší dokumentaci. +- Pravidelně kontrolujte [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) a [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) , abyste zůstali aktuální. + +## Pro uživatele + +Uživatelé by měli věnovat zvláštní pozornost: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nezbytné, pokud plánujete používat obrazy OpenIM Docker. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Chcete-li porozumět různým dostupným obrázkům a jak vybrat ten správný pro vaši architekturu. \ No newline at end of file diff --git a/docs/README_da.md b/docs/README_da.md new file mode 100644 index 000000000..d14c6adb5 --- /dev/null +++ b/docs/README_da.md @@ -0,0 +1,67 @@ +# OpenIM Server Dokumentation + +Velkommen til OpenIM Dokumentationscentret! Dette center indeholder en omfattende række vejledninger og manualer, der er designet til at hjælpe dig med at få mest muligt ud af din OpenIM-oplevelse. + +## Indholdsfortegnelse + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Vejledning om bidrag og konfigurationer for udviklere +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodningskonventioner, logningspolitikker og andre transformationsværktøjer + +------ + +## Bidrag + +Denne sektion tilbyder udviklere en detaljeret vejledning om, hvordan de kan bidrage med kode, konfigurere deres miljø og følge de tilknyttede processer. + +- [Kodekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regler og konventioner for at skrive kode i OpenIM. +- [Udviklingsguide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - En guide om, hvordan man udfører udvikling inden for OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Retningslinjer for cherry-picking-operationer. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Git-workflowen i OpenIM. +- [Initialiseringskonfigurationer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Vejledning om opsætning og initialisering af OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Sådan installeres Docker på din maskine. +- [Linux Udviklingsmiljø](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide til opsætning af udviklingsmiljøet på Linux. +- [Lokale handlinger](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Retningslinjer for, hvordan man udfører visse almindelige handlinger lokalt. +- [Offline-deploering](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metoder til at udrulle OpenIM offline. +- [Protoc-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guide til brug af protoc-værktøjer. +- [Go-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Værktøjer og biblioteker i OpenIM til Go. +- [Makefile-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Bedste praksis og værktøjer til Makefile. +- [Scriptværktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Bedste praksis og værktøjer til scripts. + +## Konverteringer + +Denne sektion introducerer forskellige konventioner og politikker inden for OpenIM, herunder kode, logfiler, versioner og meget mere. + +- [API-konverteringer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Retningslinjer og metoder til API-konverteringer. +- [Logningspolitik](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politikker og konventioner for logning i OpenIM. +- [CI/CD-handlinger](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedurer og konventioner for CI/CD. +- [Commit-konventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konventioner for kodeforpligtelser i OpenIM. +- [Mappekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Mappens struktur og konventioner inden for OpenIM. +- [Fejlkoder](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste og beskrivelser af fejlkoder. +- [Go-kodekonverteringer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konventioner og konverteringer for Go-kode. +- [Docker Image-strategi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Styringsstrategier for OpenIM Docker-billeder, der dækker flere arkitekturer og billedarkiver. +- [Logningskonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Yderligere detaljerede konventioner om logning. +- [Versionkonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Navngivnings- og styringsstrategier for OpenIM-versioner. + + +## For udviklere, bidragsydere og samfundsvedligeholdere + +### Udviklere og bidragsydere + +Hvis du er en udvikler eller nogen, der gerne vil bidrage: + +- Gør dig fortrolig med vores [Kodekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) og [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) for at sikre en problemfri bidragelse. +- Dyk ned i [Udviklingsguiden](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) for at få en idé om udviklingspraksis i OpenIM. + +### Samfundsvedligeholdere + +Som samfundsvedligeholder: + +- Sørg for, at bidrag stemmer overens med standarderne beskrevet i vores dokumentation. +- Gennemgå regelmæssigt [Logningspolitikken](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) og [Fejlkoderne](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) for at holde dig opdateret. + +## For brugere + +Brugere bør være opmærksomme på følgende: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nødvendigt, hvis du planlægger at bruge Docker-billeder af OpenIM. +- [Docker Image-strategi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - For at forstå de forskellige tilgængelige billeder og hvordan man vælger det rigtige for din arkitektur. diff --git a/docs/README_de.md b/docs/README_de.md new file mode 100644 index 000000000..101eba41b --- /dev/null +++ b/docs/README_de.md @@ -0,0 +1,66 @@ +# OpenIM-Serverdokumente + +Willkommen im OpenIM-Dokumentationshub! Dieses Zentrum bietet eine umfassende Auswahl an Leitfäden und Handbüchern, die Ihnen dabei helfen sollen, das Beste aus Ihrem OpenIM-Erlebnis herauszuholen. + +## Inhaltsverzeichnis + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Anleitung zu Beiträgen und Konfigurationen für Entwickler +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Codierungskonventionen, Protokollierungsrichtlinien und andere Transformationstools + +------ + +## Beitrag + +Dieser Abschnitt bietet Entwicklern eine detaillierte Anleitung zum Beitragen von Code, zum Einrichten ihrer Umgebung und zum Befolgen der zugehörigen Prozesse. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regeln und Konventionen zum Schreiben von Code in OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Eine Anleitung zur Durchführung der Entwicklung innerhalb von OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Richtlinien zur Rosinenpickerei. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Der Git-Workflow in OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Anleitung zum Einrichten und Initialisieren von OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - So installieren Sie Docker auf Ihrem Computer. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Anleitung zum Einrichten der Entwicklungsumgebung unter Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Richtlinien zur Durchführung bestimmter allgemeiner Aktionen vor Ort. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Methoden zur Offline-Bereitstellung von OpenIM. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Anleitung zur Verwendung von Protokolltools. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Tools und Bibliotheken in OpenIM für Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Best Practices und Tools für Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Best Practices und Tools für Skripte. + +## Konvertierungen + +In diesem Abschnitt werden verschiedene Konventionen und Richtlinien innerhalb von OpenIM vorgestellt, die Code, Protokolle, Versionen und mehr umfassen. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Richtlinien und Methoden für API-Konvertierungen. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Protokollierungsrichtlinien und -konventionen in OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Verfahren und Konventionen für CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konventionen für Code-Commits in OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Verzeichnisstruktur und Konventionen innerhalb von OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste und Beschreibungen der Fehlercodes. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konventionen und Konvertierungen für Go-Code. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Verwaltungsstrategien für OpenIM-Docker-Images, die mehrere Architekturen und Image-Repositorys umfassen. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Weitere detaillierte Konventionen zur Protokollierung. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Benennungs- und Verwaltungsstrategien für OpenIM-Versionen. + + +## Für Entwickler, Mitwirkende und Community-Betreuer + +### Entwickler und Mitwirkende + +Wenn Sie Entwickler sind oder gerne einen Beitrag leisten möchten: +- Machen Sie sich mit unseren [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) und unserem [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) vertraut, um reibungslose Beiträge zu gewährleisten. +- Tauchen Sie ein in den [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), um sich mit den Entwicklungspraktiken in OpenIM vertraut zu machen. + +### Community-Betreuer + +Als Community-Betreuer: + +- Stellen Sie sicher, dass die Beiträge den in unserer Dokumentation dargelegten Standards entsprechen. +- Überprüfen Sie regelmäßig die [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) und die [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), um auf dem Laufenden zu bleiben. + +## Für Benutzer + +Benutzer sollten besonders auf Folgendes achten: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Erforderlich, wenn Sie Docker-Images von OpenIM verwenden möchten. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Um die verschiedenen verfügbaren Bilder zu verstehen und wie Sie das richtige für Ihre Architektur auswählen. \ No newline at end of file diff --git a/docs/README_el.md b/docs/README_el.md new file mode 100644 index 000000000..68438628f --- /dev/null +++ b/docs/README_el.md @@ -0,0 +1,67 @@ +# Έγγραφα διακομιστή OpenIM + +Καλώς ήρθατε στο κέντρο τεκμηρίωσης OpenIM! Αυτό το κέντρο παρέχει μια ολοκληρωμένη σειρά οδηγών και εγχειριδίων που έχουν σχεδιαστεί για να σας βοηθήσουν να αξιοποιήσετε στο έπακρο την εμπειρία σας στο OpenIM. + +## Πίνακας περιεχομένων + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Οδηγίες για τη συνεισφορά και τις διαμορφώσεις για προγραμματιστές +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Συμβάσεις κωδικοποίησης, πολιτικές καταγραφής και άλλα εργαλεία μετασχηματισμού + +------ + +## Συνεισφορά + +Αυτή η ενότητα προσφέρει στους προγραμματιστές έναν λεπτομερή οδηγό για το πώς να συνεισφέρουν κώδικα, να ρυθμίσουν το περιβάλλον τους και να ακολουθήσουν τις σχετικές διαδικασίες. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Κανόνες και συμβάσεις για τη σύνταξη κώδικα στο OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Ένας οδηγός για το πώς να πραγματοποιήσετε ανάπτυξη στο OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Οδηγίες για τις εργασίες συλλογής κερασιών. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Η ροή εργασίας git στο OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Οδηγίες για τη ρύθμιση και την προετοιμασία του OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Πώς να εγκαταστήσετε το Docker στο μηχάνημά σας. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Οδηγός για τη ρύθμιση του περιβάλλοντος ανάπτυξης στο Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Οδηγίες για τον τρόπο εκτέλεσης ορισμένων κοινών ενεργειών σε τοπικό επίπεδο. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Μέθοδοι ανάπτυξης OpenIM εκτός σύνδεσης. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Οδηγός χρήσης εργαλείων πρωτοκόλλου. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Εργαλεία και βιβλιοθήκες στο OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Βέλτιστες πρακτικές και εργαλεία για το Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Βέλτιστες πρακτικές και εργαλεία για σενάρια. + +## Μετατροπές + +Αυτή η ενότητα εισάγει διάφορες συμβάσεις και πολιτικές στο OpenIM, που περιλαμβάνουν κώδικα, αρχεία καταγραφής, εκδόσεις και άλλα. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Οδηγίες και μέθοδοι για μετατροπές API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Πολιτικές και συμβάσεις καταγραφής στο OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Διαδικασίες και συμβάσεις για CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Συμβάσεις για δεσμεύσεις κώδικα στο OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Δομή καταλόγου και συμβάσεις στο OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Λίστα και περιγραφές κωδικών σφαλμάτων. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Συμβάσεις και μετατροπές για τον κώδικα Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Στρατηγικές διαχείρισης για εικόνες OpenIM Docker, που εκτείνονται σε πολλαπλές αρχιτεκτονικές και αποθετήρια εικόνων. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Περαιτέρω λεπτομερείς συμβάσεις για την υλοτομία. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Στρατηγικές ονομασίας και διαχείρισης για εκδόσεις OpenIM. + + +## Για προγραμματιστές, συνεισφέροντες και συντηρητές κοινότητας + +### Προγραμματιστές & Συνεισφέροντες + +Εάν είστε προγραμματιστής ή κάποιος που επιθυμεί να συνεισφέρει: + +- Εξοικειωθείτε με τις [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) και [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) για να εξασφαλίσετε ομαλή συνεισφορά. +- Βουτήξτε στον [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) για να δείτε τις πρακτικές ανάπτυξης στο OpenIM. + +### Συντηρητές της Κοινότητας + +Ως συντηρητής κοινότητας: + +- Βεβαιωθείτε ότι οι συνεισφορές ευθυγραμμίζονται με τα πρότυπα που περιγράφονται στην τεκμηρίωσή μας. +- Να ελέγχετε τακτικά την [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) και τους [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) για να ενημερώνεστε. + +## Για Χρήστες + +Οι χρήστες θα πρέπει να δώσουν ιδιαίτερη προσοχή: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Απαραίτητο εάν σκοπεύετε να χρησιμοποιήσετε εικόνες Docker του OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Για να κατανοήσετε τις διάφορες διαθέσιμες εικόνες και πώς να επιλέξετε τη σωστή για την αρχιτεκτονική σας. \ No newline at end of file diff --git a/docs/README_eo.md b/docs/README_eo.md new file mode 100644 index 000000000..d7ae8c08c --- /dev/null +++ b/docs/README_eo.md @@ -0,0 +1,67 @@ +# OpenIM Server Docs + +Bonvenon al la OpenIM Dokumenta nabo! Ĉi tiu centro disponigas ampleksan gamon da gvidiloj kaj manlibroj desegnitaj por helpi vin eltiri la plej grandan parton de via OpenIM-sperto. + +## Enhavtabelo + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Gvidilo pri kontribuado kaj agordoj por programistoj +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodigaj konvencioj, registradaj politikoj kaj aliaj transformaj iloj + +------ + +## Kontribui + +Ĉi tiu sekcio ofertas al programistoj detalan gvidilon pri kiel kontribui kodon, agordi sian medion kaj sekvi la rilatajn procezojn. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Reguloj kaj konvencioj por skribi kodon en OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Gvidilo pri kiel efektivigi disvolviĝon ene de OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Gvidlinioj pri ĉeriz-plukaj operacioj. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - La git-laborfluo en OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Gvidilo pri agordo kaj pravalorigo de OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Kiel instali Docker sur via maŝino. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Gvidilo por agordi la evolumedion en Linukso. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Gvidlinioj pri kiel efektivigi certajn komunajn agojn loke. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metodoj por disfaldi OpenIM eksterrete. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Gvidilo pri uzado de protokaj iloj. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Iloj kaj bibliotekoj en OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Plej bonaj praktikoj kaj iloj por Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Plej bonaj praktikoj kaj iloj por skriptoj. + +## Konvertiĝoj + +Ĉi tiu sekcio enkondukas diversajn konvenciojn kaj politikojn ene de OpenIM, ampleksante kodon, protokolojn, versiojn kaj pli. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Gvidlinioj kaj metodoj por API-konvertoj. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Registrado de politikoj kaj konvencioj en OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Proceduroj kaj konvencioj por CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvencioj por kodoj en OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Dosierujo-strukturo kaj konvencioj ene de OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Listo kaj priskriboj de erarkodoj. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvencioj kaj konvertiĝoj por Go-kodo. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Administradstrategioj por OpenIM Docker-bildoj, ampleksante plurajn arkitekturojn kaj bilddeponejojn. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Pliaj detalaj konvencioj pri arbohakado. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategioj pri nomado kaj administrado por OpenIM-versioj. + + +## Por Programistoj, Kontribuantoj kaj Komunumaj Prizorgantoj + +### Programistoj kaj Kontribuantoj + +Se vi estas programisto aŭ iu fervora kontribui: + +- Familiariĝu kun niaj [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) por certigi glatajn kontribuojn. +- Plonĝu en la[Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) por ekkompreni la evolupraktikojn en OpenIM. + +### Komunumaj Prizorgantoj + +Kiel komunuma prizorganto: + +- Certigu, ke kontribuoj kongruas kun la normoj skizitaj en nia dokumentaro. +- Regule reviziu la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) kaj [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) por resti ĝisdatigita. + +## Por Uzantoj + +Uzantoj devas aparte atenti: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necesas se vi planas uzi Docker-bildojn de OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Por kompreni la malsamajn bildojn disponeblajn kaj kiel elekti la ĝustan por via arkitekturo. \ No newline at end of file diff --git a/docs/README_es.md b/docs/README_es.md new file mode 100644 index 000000000..96523120a --- /dev/null +++ b/docs/README_es.md @@ -0,0 +1,67 @@ +# Documentos del servidor OpenIM + +¡Bienvenido al centro de documentación de OpenIM! Este centro proporciona una amplia gama de guías y manuales diseñados para ayudarle a aprovechar al máximo su experiencia OpenIM. + +## Tabla de contenido + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Orientación sobre contribuciones y configuraciones para desarrolladores +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenciones de codificación, políticas de registro y otras herramientas de transformación + +------ + +## Contribuir + +Esta sección ofrece a los desarrolladores una guía detallada sobre cómo contribuir con código, configurar su entorno y seguir los procesos asociados. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Reglas y convenciones para escribir código en OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Una guía sobre cómo realizar el desarrollo dentro de OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Directrices sobre operaciones de selección selectiva. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - El flujo de trabajo de git en OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Orientación sobre la configuración e inicialización de OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cómo instalar Docker en su máquina. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guía para configurar el entorno de desarrollo en Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Lineamientos sobre cómo llevar a cabo determinadas acciones comunes a nivel local. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Métodos de implementación de OpenIM sin conexión. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guía sobre el uso de herramientas de protocolo. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Herramientas y bibliotecas en OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Mejores prácticas y herramientas para Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Mejores prácticas y herramientas para scripts. + +## Conversiones + +Esta sección presenta varias convenciones y políticas dentro de OpenIM, que abarcan código, registros, versiones y más. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Directrices y métodos para conversiones de API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Políticas y convenciones de registro en OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedimientos y convenciones para CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenciones para confirmaciones de código en OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Estructura de directorios y convenciones dentro de OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista y descripciones de códigos de error. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenciones y conversiones para código Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Estrategias de gestión para imágenes OpenIM Docker, que abarcan múltiples arquitecturas y repositorios de imágenes. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Convenciones más detalladas sobre el registro. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Estrategias de nomenclatura y gestión de versiones OpenIM. + + +## Para desarrolladores, contribuyentes y mantenedores de la comunidad + +### Desarrolladores y colaboradores + +Si eres desarrollador o alguien interesado en contribuir: + +- Familiarícese con nuestras [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) y [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) to ensure smooth contributions. +- Sumérgete en la [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) para familiarizarte con las prácticas de desarrollo en OpenIM. + +### Mantenedores de la comunidad + +Como mantenedor de la comunidad: + +- Asegúrese de que las contribuciones se alineen con los estándares descritos en nuestra documentación. +- Revise periódicamente la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) y los [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) para mantenerse actualizado. + +## Para usuarios + +Los usuarios deben prestar especial atención a: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necesario si planea utilizar imágenes Docker de OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Comprender las diferentes imágenes disponibles y cómo elegir la adecuada para su arquitectura. \ No newline at end of file diff --git a/docs/README_fa.md b/docs/README_fa.md new file mode 100644 index 000000000..43056647a --- /dev/null +++ b/docs/README_fa.md @@ -0,0 +1,67 @@ +# اسناد سرور OpenIM + +به مرکز اسناد OpenIM خوش آمدید! این مرکز طیف گسترده ای از راهنماها و راهنماها را ارائه می دهد که به شما کمک می کند تا از تجربه OpenIM خود بیشترین بهره را ببرید. + +## فهرست مطالب + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - راهنمایی در مورد مشارکت و تنظیمات برای توسعه دهندگان +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - کنوانسیون های کدگذاری، سیاست های ورود به سیستم و سایر ابزارهای تبدیل + +------ + +## مشارکت + +این بخش به توسعه دهندگان راهنمای دقیقی در مورد نحوه مشارکت کد، تنظیم محیط خود و پیروی از فرآیندهای مرتبط ارائه می دهد. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - قوانین و مقررات برای نوشتن کد در OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - راهنمای نحوه انجام توسعه در OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - دستورالعمل عملیات چیدن گیلاس +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - گردش کار git در OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - راهنمایی در مورد راه اندازی و مقداردهی اولیه OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - چگونه داکر را روی دستگاه خود نصب کنیم. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - راهنمای راه اندازی محیط توسعه در لینوکس. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - رهنمودهایی در مورد نحوه انجام برخی از اقدامات مشترک به صورت محلی. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - روش های استقرار OpenIM به صورت آفلاین +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - راهنمای استفاده از ابزار پروتک +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - ابزارها و کتابخانه ها در OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - بهترین روش ها و ابزارها برای Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - بهترین روش ها و ابزارها برای اسکریپت ها. + +## تبدیل ها + +این بخش قراردادها و سیاست‌های مختلفی را در OpenIM معرفی می‌کند که شامل کد، گزارش‌ها، نسخه‌ها و موارد دیگر می‌شود. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - دستورالعمل ها و روش های تبدیل API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - سیاست‌های ورود به سیستم و قراردادها در OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - رویه ها و قراردادها برای CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - کنوانسیون‌ها برای تعهدات کد در OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - ساختار دایرکتوری و قراردادها در OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - لیست و توضیحات کدهای خطا +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - کنوانسیون ها و تبدیل ها برای کد Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - استراتژی های مدیریتی برای تصاویر OpenIM Docker، شامل چندین معماری و مخازن تصویر. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - کنوانسیون های دقیق تر در مورد ورود به سیستم. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - استراتژی های نامگذاری و مدیریت برای نسخه های OpenIM. + + +## برای توسعه‌دهندگان، مشارکت‌کنندگان و نگهبانان انجمن + +### توسعه دهندگان و مشارکت کنندگان + +اگر توسعه‌دهنده هستید یا کسی که مشتاق مشارکت است: + +- - برای اطمینان از مشارکت های روان، با [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) و [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) ما آشنا شوید. +- - در [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) شیرجه بزنید تا از شیوه های توسعه در OpenIM مطلع شوید. + +### نگهبانان جامعه + +به عنوان یک نگهدارنده جامعه: + +- اطمینان حاصل کنید که مشارکت ها با استانداردهای ذکر شده در اسناد ما مطابقت دارند. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) و [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) را به‌طور مرتب مرور کنید تا به‌روز بمانید. + +## برای کاربران + +کاربران باید توجه ویژه ای به موارد زیر داشته باشند: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - اگر قصد دارید از تصاویر Docker OpenIM استفاده کنید، ضروری است. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - برای درک تصاویر مختلف موجود و نحوه انتخاب تصویر مناسب برای معماری خود. \ No newline at end of file diff --git a/docs/README_fi.md b/docs/README_fi.md new file mode 100644 index 000000000..9678763d9 --- /dev/null +++ b/docs/README_fi.md @@ -0,0 +1,67 @@ +# OpenIM Server Docs + +Tervetuloa OpenIM-dokumentaatiokeskukseen! Tämä keskus tarjoaa kattavan valikoiman oppaita ja oppaita, jotka on suunniteltu auttamaan sinua saamaan kaiken irti OpenIM-kokemuksestasi. + +## Sisällysluettelo + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Ohjeita kehittäjille osallistumiseen ja määrityksiin +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Koodauskäytännöt, lokikäytännöt ja muut muunnostyökalut + +------ + +## Contrib + +Tämä osio tarjoaa kehittäjille yksityiskohtaisen oppaan koodin lisäämisestä, ympäristön määrittämisestä ja siihen liittyvien prosessien seuraamisesta. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Säännöt ja käytännöt koodin kirjoittamiselle OpenIM:ssä. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Opas kehitystyön toteuttamiseen OpenIM:ssä. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Kirsikoiden poimimista koskevat ohjeet. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Git-työnkulku OpenIM:ssä. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Ohjeita OpenIM:n käyttöönottoon ja alustamiseen. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Kuinka asentaa Docker koneellesi. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Ohje kehitysympäristön määrittämiseen Linuxissa. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Ohjeita tiettyjen yhteisten toimien toteuttamiseen paikallisesti. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM:n offline-käyttöönottomenetelmät. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Opas protokollatyökalujen käyttöön. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM for Go -työkalut ja kirjastot. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefilen parhaat käytännöt ja työkalut. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Skriptien parhaat käytännöt ja työkalut. + +## Tulokset + +Tässä osiossa esitellään erilaisia OpenIM:n käytäntöjä ja käytäntöjä, jotka kattavat koodin, lokit, versiot ja paljon muuta. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Ohjeita ja menetelmiä API-muunnoksille. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Kirjauskäytännöt ja käytännöt OpenIM:ssä. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD:n menettelyt ja käytännöt. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM:n koodisitoumusten käytännöt. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Hakemistorakenne ja käytännöt OpenIM:ssä. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Luettelo ja kuvaukset virhekoodeista. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go-koodin sopimukset ja muunnokset. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Hallintastrategiat OpenIM Docker -kuville, jotka kattavat useita arkkitehtuureja ja kuvavarastoja. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Tarkemmat hakkuiden käytännöt. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Nimeämis- ja hallintastrategiat OpenIM-versioille. + + +## Kehittäjille, avustajille ja yhteisön ylläpitäjille + +### Kehittäjät ja avustajat + +Jos olet kehittäjä tai joku, joka haluaa osallistua: + +- Tutustu [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md)- ja [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) -käytäntöihimme varmistaaksesi sujuvan osallistumisen. +- Sukella [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) saadaksesi tietoa OpenIM:n kehityskäytännöistä. + +### Yhteisön ylläpitäjät + +Yhteisön ylläpitäjänä: + +- Varmista, että osallistumiset ovat asiakirjoissamme esitettyjen standardien mukaisia. +- Tarkista säännöllisesti [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) ja [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) pysyäksesi ajan tasalla. + +## Käyttäjille + +Käyttäjien tulee kiinnittää erityistä huomiota: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Välttämätön, jos aiot käyttää OpenIM:n Docker-kuvia. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Ymmärtääksesi saatavilla olevat erilaiset kuvat ja kuinka valita oikea arkkitehtuuriisi. \ No newline at end of file diff --git a/docs/README_fr.md b/docs/README_fr.md new file mode 100644 index 000000000..747ef5349 --- /dev/null +++ b/docs/README_fr.md @@ -0,0 +1,66 @@ +# Documentation du serveur OpenIM + +Bienvenue dans le hub de documentation OpenIM ! Ce centre propose une gamme complète de guides et de manuels conçus pour vous aider à tirer le meilleur parti de votre expérience OpenIM. + +## Table des matières + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Conseils sur la contribution et les configurations pour les développeurs +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Conventions de codage, politiques de journalisation et autres outils de transformation + +------ + +## Contribuer + +Cette section propose aux développeurs un guide détaillé sur la façon de contribuer au code, de configurer leur environnement et de suivre les processus associés. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Règles et conventions pour écrire du code dans OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Un guide sur la façon de réaliser du développement au sein d'OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Lignes directrices sur les opérations de triage. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Le flux de travail git dans OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Conseils sur la configuration et l’initialisation d’OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Comment installer Docker sur votre machine. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide pour configurer l'environnement de développement sous Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Des lignes directrices pour mener localement certaines actions communes. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Méthodes de déploiement d'OpenIM hors ligne. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guide sur l'utilisation des outils de protocole. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Outils et bibliothèques dans OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Meilleures pratiques et outils pour Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Meilleures pratiques et outils pour les scripts. + +## Conversions + +Cette section présente diverses conventions et politiques au sein d'OpenIM, englobant le code, les journaux, les versions, etc. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Lignes directrices et méthodes pour les conversions API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politiques et conventions de journalisation dans OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Politiques et conventions de journalisation dans OpenIM. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Politiques et conventions de journalisation dans OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Structure de répertoire et conventions au sein d'OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste et descriptions des codes d'erreur. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Conventions et conversions pour le code Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Stratégies de gestion des images OpenIM Docker, couvrant plusieurs architectures et référentiels d'images. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Conventions plus détaillées sur la journalisation. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Stratégies de nommage et de gestion pour les versions OpenIM. + + +## Pour les développeurs, les contributeurs et les responsables de la communauté + +### Développeurs et contributeurs + +Si vous êtes un développeur ou quelqu'un désireux de contribuer : +- Familiarisez-vous avec nos [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) et notre [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) pour garantir des contributions fluides. +- Plongez dans le [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) pour vous familiariser avec les pratiques de développement dans OpenIM. + +### Responsables de la communauté + +En tant que responsable de la communauté : + +- EAssurez-vous que les contributions sont conformes aux normes décrites dans notre documentation. +- onsultez régulièrement la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) et les [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) pour rester à jour. + +## Pour les utilisateurs + +Les utilisateurs doivent prêter une attention particulière à : + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nécessaire si vous prévoyez d'utiliser des images Docker d'OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Pour comprendre les différentes images disponibles et comment choisir celle qui convient à votre architecture. \ No newline at end of file diff --git a/docs/README_hu.md b/docs/README_hu.md new file mode 100644 index 000000000..62cb54bb2 --- /dev/null +++ b/docs/README_hu.md @@ -0,0 +1,67 @@ +# OpenIM Server Docs + +Üdvözöljük az OpenIM dokumentációs központjában! Ez a központ útmutatók és kézikönyvek átfogó választékát kínálja, amelyek célja, hogy a legtöbbet hozza ki az OpenIM-élményből. + +## Tartalomjegyzék + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Útmutató a fejlesztőknek a hozzájáruláshoz és a konfigurációkhoz +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kódolási konvenciók, naplózási szabályzatok és egyéb átalakítási eszközök + +------ + +## Contrib + +Ez a rész részletes útmutatót nyújt a fejlesztőknek a kód hozzáadásával, a környezet beállításával és a kapcsolódó folyamatok követésével kapcsolatban. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Az OpenIM kódírásának szabályai és konvenciói. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Útmutató az OpenIM-en belüli fejlesztés végrehajtásához. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Útmutató a cseresznyeszedési műveletekhez. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - A git munkafolyamat az OpenIM-ben. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Útmutató az OpenIM beállításához és inicializálásához. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - A Docker telepítése a gépére. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide to set up the development environment on Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Útmutató a fejlesztői környezet beállításához Linuxon. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Az OpenIM offline üzembe helyezésének módszerei. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Útmutató a protokolleszközök használatához. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Eszközök és könyvtárak az OpenIM for Go-ban. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - A Makefile legjobb gyakorlatai és eszközei. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Bevált módszerek és eszközök a szkriptekhez. + +## Conversions + +Ez a rész az OpenIM különféle konvencióit és szabályzatait mutatja be, beleértve a kódot, a naplókat, a verziókat és egyebeket. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Irányelvek és módszerek az API-konverziókhoz. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Naplózási szabályzatok és konvenciók az OpenIM-ben. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD eljárások és konvenciók. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - A kód véglegesítésének konvenciói az OpenIM-ben. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Címtárszerkezet és konvenciók az OpenIM-en belül. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - A hibakódok listája és leírása. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvenciók és átalakítások a Go kódhoz. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Kezelési stratégiák az OpenIM Docker-képekhez, amelyek több architektúrára és képtárra is kiterjednek. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - További részletes egyezmények a fakitermelésről. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Elnevezési és kezelési stratégiák az OpenIM verziókhoz. + + +## Fejlesztőknek, közreműködőknek és közösségi fenntartóknak + +### Fejlesztők és közreműködők + +Ha Ön fejlesztő vagy valaki, aki szeretne hozzájárulni: + +- A zökkenőmentes hozzájárulás érdekében ismerkedjen meg [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) és [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)-val. +- Merüljön el a [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), hogy megismerje az OpenIM fejlesztési gyakorlatát. + +### Közösségfenntartók + +Közösségfenntartóként: + +- Győződjön meg arról, hogy a hozzájárulások megfelelnek a dokumentációnkban felvázolt szabványoknak. +- Rendszeresen tekintse át a [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) és a [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), hogy naprakész maradjon. + +## Felhasználóknak + +A felhasználóknak különös figyelmet kell fordítaniuk a következőkre: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Szükséges, ha az OpenIM Docker-képeit szeretné használni. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Hogy megértse a rendelkezésre álló különböző képeket, és hogyan válassza ki a megfelelőt az építészetéhez. \ No newline at end of file diff --git a/docs/README_id.md b/docs/README_id.md new file mode 100644 index 000000000..988e25efe --- /dev/null +++ b/docs/README_id.md @@ -0,0 +1,67 @@ +# Dokumen Server OpenIM + +Selamat datang di pusat Dokumentasi OpenIM! Pusat ini menyediakan berbagai panduan dan manual yang komprehensif untuk membantu Anda memaksimalkan pengalaman Anda dengan OpenIM. + +## Daftar Isi + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Panduan tentang kontribusi dan konfigurasi untuk pengembang +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konvensi pengkodean, kebijakan logging, dan alat transformasi lainnya + +------ + +## Contrib + +Bagian ini menawarkan panduan rinci bagi pengembang tentang cara berkontribusi kode, menyiapkan lingkungan mereka, dan mengikuti proses yang terkait. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Aturan dan konvensi untuk menulis kode di OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Panduan tentang cara melakukan pengembangan di dalam OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Pedoman tentang operasi cherry-picking. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Alur kerja git di OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Panduan untuk mengatur dan menginisialisasi OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cara memasang Docker di mesin Anda. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Panduan untuk menyiapkan lingkungan pengembangan di Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Pedoman tentang cara melakukan beberapa tindakan umum secara lokal. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metode penyebaran OpenIM secara offline. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Panduan tentang menggunakan alat protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Alat dan perpustakaan di OpenIM untuk Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Praktik terbaik dan alat untuk Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Praktik terbaik dan alat untuk skrip. + +## Conversions + +Bagian ini memperkenalkan berbagai konvensi dan kebijakan dalam OpenIM, meliputi kode, log, versi, dan lainnya. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Pedoman dan metode untuk konversi API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Kebijakan dan konvensi logging di OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Prosedur dan konvensi untuk CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvensi untuk commit kode di OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktur direktori dan konvensi dalam OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Daftar dan deskripsi kode kesalahan. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvensi dan konversi untuk kode Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategi manajemen gambar Docker OpenIM, mencakup berbagai arsitektur dan repositori gambar. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Konvensi logging yang lebih detail. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategi penamaan dan manajemen versi OpenIM. + + +## Untuk Pengembang, Kontributor, dan Pemelihara Komunitas + +### Pengembang & Kontributor + +Jika Anda seorang pengembang atau seseorang yang ingin berkontribusi: + +- Kenali [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) dan [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) kami untuk memastikan kontribusi yang lancar. +- Pelajari [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) untuk memahami praktik pengembangan di OpenIM. + +### Pemelihara Komunitas + +Sebagai pemelihara komunitas: + +- Pastikan kontribusi sesuai dengan standar yang diuraikan dalam dokumentasi kami. +- Tinjau secara teratur [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) dan [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) untuk tetap terupdate. + +## Untuk Pengguna + +Pengguna harus memberikan perhatian khusus kepada: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Diperlukan jika Anda berencana menggunakan gambar Docker dari OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Untuk memahami gambar yang tersedia dan bagaimana memilih yang tepat untuk arsitektur Anda. diff --git a/docs/README_it.md b/docs/README_it.md new file mode 100644 index 000000000..76e41b5e7 --- /dev/null +++ b/docs/README_it.md @@ -0,0 +1,67 @@ +# Documentazione del Server OpenIM + +Benvenuti al centro documentazione di OpenIM! Questo centro offre una gamma completa di guide e manuali progettati per aiutarvi a ottenere il massimo dalla vostra esperienza con OpenIM. + +## Indice dei Contenuti + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Guida al contributo e alle configurazioni per gli sviluppatori +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenzioni di codifica, politiche di registrazione e altri strumenti di trasformazione + +------ + +## Contrib + +Questa sezione offre agli sviluppatori una guida dettagliata su come contribuire al codice, configurare il loro ambiente e seguire i processi associati. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regole e convenzioni per scrivere codice in OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Una guida su come svolgere lo sviluppo all'interno di OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Linee guida sulle operazioni di cherry-picking. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Il flusso di lavoro git in OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Guida per configurare e inizializzare OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Come installare Docker sul tuo dispositivo. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guida per configurare l'ambiente di sviluppo su Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Linee guida su come eseguire determinate azioni comuni localmente. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metodi per distribuire OpenIM offline. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guida all'uso degli strumenti protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Strumenti e librerie in OpenIM per Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Migliori pratiche e strumenti per Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Migliori pratiche e strumenti per gli script. + +## Conversions + +Questa sezione introduce varie convenzioni e politiche all'interno di OpenIM, che comprendono codice, registrazioni, versioni e altro. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Linee guida e metodi per le conversioni API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politiche e convenzioni di registrazione in OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedure e convenzioni per CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenzioni per i commit di codice in OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struttura delle directory e convenzioni all'interno di OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Elenco e descrizioni dei codici di errore. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenzioni e conversioni per il codice Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie di gestione delle immagini Docker di OpenIM, che coprono più architetture e repository di immagini. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Ulteriori dettagliate convenzioni sulla registrazione. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie di denominazione e gestione delle versioni di OpenIM. + + +## Per Sviluppatori, Contributori e Manutentori della Comunità + +### Sviluppatori & Contributori + +Se sei uno sviluppatore o qualcuno interessato a contribuire: + +- Familiarizza con le nostre [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) e [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) per garantire contributi fluidi. +- Approfondisci la [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) per ottenere una conoscenza delle pratiche di sviluppo in OpenIM. + +### Manutentori della Comunità + +Come manutentore della comunità: + +- Assicurati che i contributi siano in linea con gli standard delineati nella nostra documentazione. +- Rivedi regolarmente la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) e [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) per rimanere aggiornato. + +## Per gli Utenti + +Gli utenti dovrebbero prestare particolare attenzione a: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necessario se si prevede di utilizzare le immagini Docker di OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Per comprendere le diverse immagini disponibili e come scegliere quella giusta per la propria architettura. diff --git a/docs/README_ja.md b/docs/README_ja.md new file mode 100644 index 000000000..3971037fb --- /dev/null +++ b/docs/README_ja.md @@ -0,0 +1,67 @@ +# OpenIMサーバードキュメンテーション + +OpenIMドキュメンテーションハブへようこそ!このセンターでは、OpenIM体験を最大限に活用するための包括的なガイドとマニュアルを提供しています。 + +## 目次 + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 開発者向けの貢献と設定に関するガイダンス +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - コーディング規約、ログポリシー、その他の変換ツール + +------ + +## 投稿 + +このセクションでは、開発者がコードを貢献し、環境を設定し、関連するプロセスに従う方法についての詳細なガイドを提供します。 + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIMでのコード記述のルールと規約。 +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM内での開発を行うためのガイド。 +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - チェリーピッキング操作のガイドライン。 +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIMにおけるGitのワークフロー。 +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIMの設定と初期化に関するガイダンス。 +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - マシンにDockerをインストールする方法。 +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux上での開発環境の設定ガイド。 +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - ローカルで一般的なアクションを実行する方法に関するガイドライン。 +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIMをオフラインでデプロイする方法。 +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - protocツールの使用ガイド。 +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - GoのためのOpenIM内のツールとライブラリ。 +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefileのベストプラクティスとツール。 +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - スクリプトのベストプラクティスとツール。 + +## Conversions + +このセクションでは、OpenIM内のさまざまな規約とポリシーを紹介します。これには、コード、ログ、バージョンなどが含まれます。 + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API変換のためのガイドラインと方法。 +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIMにおけるログポリシーと規約。 +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CDの手順と規約。 +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIMでのコードコミットのための規約。 +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM内のディレクトリ構造と規約。 +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - エラーコードのリストと説明。 +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Goコードのための規約と変換。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 複数のアーキテクチャとイメージリポジトリにまたがるOpenIM Dockerイメージの管理戦略。 +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - ロギングに関するさらに詳細な規約。 +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIMバージョンの命名と管理戦略。 + + +## 開発者、コントリビューター、コミュニティメンテナー向け + +### 開発者およびコントリビューター + +開発者または貢献に熱心な方へ: + +- スムーズな貢献を確実にするために、私たちの[Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md)と[Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)に慣れ親しみましょう。 +- OpenIMの開発実践に慣れるために、[Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md)をご覧ください。 + +### コミュニティメンテナー + +コミュニティメンテナーとして: + +- 貢献が私たちのドキュメンテーションで概説された基準に沿っていることを確認してください。 +- 最新の情報を得るために、定期的に[Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md)と[Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md)をレビューしてください。 + +## ユーザー向け + +ユーザーは特に以下の点に注意してください: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIMのDockerイメージを使用する予定の場合に必要です。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 利用可能なさまざまなイメージを理解し、アーキテクチャに適したものを選択する方法。 diff --git a/docs/README_ko.md b/docs/README_ko.md new file mode 100644 index 000000000..9cd728c5d --- /dev/null +++ b/docs/README_ko.md @@ -0,0 +1,67 @@ +# OpenIM 서버 문서 + +OpenIM 문서 허브에 오신 것을 환영합니다! 이 센터는 OpenIM 경험을 최대한 활용하는 데 도움이 되도록 다양한 가이드와 매뉴얼을 제공합니다. + +## 목차 + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 개발자를 위한 기여 및 구성에 대한 안내 +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 코딩 규칙, 로깅 정책 및 기타 변환 도구 + +------ + +## 기여 + +이 섹션은 개발자들에게 코드를 기여하는 방법, 환경을 설정하는 방법 및 관련 프로세스를 따르는 방법에 대한 자세한 가이드를 제공합니다. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM에서 코드를 작성하기 위한 규칙 및 규약. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM 내에서 개발을 수행하는 방법에 대한 가이드. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 체리피킹 작업에 대한 지침. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM에서의 깃 워크플로우. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM 설정 및 초기화에 대한 안내. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 컴퓨터에 도커를 설치하는 방법. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - 리눅스에서 개발 환경을 설정하는 가이드. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 일부 일반적인 작업을 로컬에서 수행하는 방법에 대한 지침. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 오프라인에서 OpenIM을 배포하는 방법. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 프로토콜 도구 사용에 대한 가이드. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go용 OpenIM 도구 및 라이브러리. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - 메이크파일을 위한 모범 사례 및 도구. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 스크립트를 위한 모범 사례 및 도구. + +## 전환 + +이 섹션에서는 코드, 로그, 버전 등을 포함하는 OpenIM 내의 다양한 규칙과 정책을 소개합니다. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 변환을 위한 지침 및 방법. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM의 로깅 정책 및 관습. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 절차 및 관습. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM에서 코드 커밋을 위한 관습. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 내의 디렉토리 구조 및 관습. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 오류 코드 목록 및 설명. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 코드를 위한 관습 및 변환. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 다양한 아키텍처 및 이미지 저장소를 아우르는 OpenIM Docker 이미지 관리 전략. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 로깅에 대한 추가적인 상세한 관습. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 버전의 명명 및 관리 전략. + + +## 개발자, 기여자 및 커뮤니티 관리자를 위한 정보 + +### 개발자 및 기여자 + +개발자이거나 기여에 관심이 있다면: + +- 원활한 기여를 위해 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 및 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)에 익숙해지십시오. +- OpenIM에서의 개발 관행을 파악하려면 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md)를 참조하십시오. + +### 커뮤니티 관리자 + +커뮤니티 관리자로서: + +- 기여가 우리 문서에 명시된 표준에 부합하는지 확인하십시오. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 및 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md)를 정기적으로 검토하여 최신 정보를 유지하십시오. + +## 사용자를 위한 정보 + +사용자는 특히 다음 사항에 주의를 기울여야 합니다: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM의 Docker 이미지를 사용할 계획이라면 필요합니다. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 사용 가능한 다양한 이미지를 이해하고 아키텍처에 적합한 이미지를 선택하는 방법. diff --git a/docs/README_ml.md b/docs/README_ml.md new file mode 100644 index 000000000..7f3aa8c0d --- /dev/null +++ b/docs/README_ml.md @@ -0,0 +1,67 @@ +# OpenIM സെർവർ ഡോക്യുമെന്റേഷൻ + +OpenIM ഡോക്യുമെന്റേഷൻ ഹബ്ബിലേക്ക് സ്വാഗതം! ഈ കേന്ദ്രം OpenIM അനുഭവത്തിൽ നിന്ന് പരമാവധി ഉപയോഗം നേടാൻ സഹായിക്കുന്ന വ്യാപകമായ നിർദേശങ്ങളുടെയും മാനുവലുകളുടെയും ശ്രേണി നൽകുന്നു. + +## ഉള്ളടക്കം + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - ഡെവലപ്പർമാർക്കുള്ള സംഭാവനകൾ നൽകുന്നതിനും കോൺഫിഗറേഷനുകൾക്കുള്ള നിർദേശങ്ങൾ +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - കോഡിംഗ് കൺവെൻഷനുകൾ, ലോഗ്ഗിംഗ് നയങ്ങൾ, മറ്റ് പരിവർത്തന ഉപകരണങ്ങൾ + +------ + +## Contrib + +ഈ ഭാഗം ഡെവലപ്പർമാർക്ക് കോഡ് സംഭാവന നൽകുന്നതിന്റെയും അവരുടെ പരിസ്ഥിതി സജ്ജമാക്കുന്നതിന്റെയും ബന്ധപ്പെട്ട പ്രക്രിയകൾ പിന്തുടരുന്നതിന്റെയും വിശദമായ ഗൈഡ് നൽകുന്നു. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM-ൽ കോഡ് എഴുതുന്നതിന്റെ നിയമങ്ങൾ കൺവെൻഷനുകൾ. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM-ൽ വികസനം നടത്തുന്നതിന്റെ ഗൈഡ്. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - ചെറി-പിക്കിംഗ് ഓപ്പറേഷനുകൾക്കുള്ള മാർഗ്ഗനിർദേശങ്ങൾ. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM-ൽ ഗിറ്റിന്റെ വർക്ക്ഫ്ലോ. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM സജ്ജമാക്കുന്നതിനും ആരംഭിക്കുന്നതിനും നിർദേശങ്ങൾ. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - നിങ്ങളുടെ യന്ത്രത്തിൽ ഡോക്കർ ഇൻസ്റ്റാൾ ചെയ്യുന്ന രീതി. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - ലിനക്സിൽ വികസന പരിസ്ഥിതി സജ്ജമാക്കുന്നതിന്റെ ഗൈഡ്. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - ചില പൊതുവായ നടപടികൾ പ്രദേശികമായി നടത്തുന്നതിന്റെ മാർഗ്ഗനിർദേശങ്ങൾ. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM ഓഫ്‌ലൈൻ ഡിപ്ലോയ് ചെയ്യുന്ന രീതികൾ. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - പ്രോട്ടോക് ഉപകരണങ്ങൾ ഉപയോഗിക്കുന്ന ഗൈഡ്. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go വേണ്ടി OpenIM-ൽ ഉള്ള ഉപകരണങ്ങളും ലൈബ്രറികളും. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile ഉപകരണങ്ങളുടെയും മികച്ച പ്രാക്ടീസുകളുടെയും ഗൈഡ്. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - സ്ക്രിപ്റ്റുകൾക്കുള്ള മികച്ച പ്രാക്ടീസുകളും ഉപകരണങ്ങളും. + +## Conversions + +ഈ ഭാഗം OpenIM-ൽ ഉള്ള വിവിധ കൺവെൻഷനുകളെയും നയങ്ങളെയും ആവിഷ്കരിക്കുന്നു, ഇതിൽ കോഡ്, ലോഗുകൾ, പതിപ്പുകൾ എന്നിവ ഉൾപ്പെടുന്നു. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API കൺവെർഷനുകൾക്കുള്ള നിർദേശങ്ങൾ രീതികൾ. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM-ൽ ലോഗ്ഗിംഗ് നയങ്ങൾ കൺവെൻഷനുകൾ. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD പ്രക്രിയകൾ കൺവെൻഷനുകൾ. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM-ൽ കോഡ് കമ്മിറ്റുകൾക്കുള്ള കൺവെൻഷനുകൾ. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM-ൽ ഡയറക്ടറി ഘടന കൺവെൻഷനുകൾ. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - പിശക് കോഡുകളുടെ പട്ടിക വിവരണങ്ങൾ. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go കോഡിനുള്ള കൺവെൻഷനുകൾ പരിവർത്തനങ്ങൾ. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - വിവിധ ആർക്കിടെക്ചറുകൾ ഇമേജ് റെപ്പോസിറ്ററികൾ ഉൾപ്പെടുന്ന OpenIM Docker ഇമേജുകളുടെ മാനേജ്മെന്റ് സ്ട്രാറ്റജീസ്. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - ലോഗ്ഗിംഗിന്റെ കൂടുതൽ വിശദമായ കൺവെൻഷനുകൾ. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM പതിപ്പുകൾക്കുള്ള നാമകരണ മാനേജ്മെന്റ് സ്ട്രാറ്റജീസ്. + + +## ഡെവലപ്പർമാർക്കും, സംഭാവനകൾ നൽകുന്നവർക്കും, കമ്മ്യൂണിറ്റി മെയിന്റെയിനർമാർക്കും + +### ഡെവലപ്പർമാർ & സംഭാവനകൾ നൽകുന്നവർ + +നിങ്ങൾ ഒരു ഡെവലപ്പർ അല്ലെങ്കിൽ സംഭാവനകൾ നൽകാൻ ആഗ്രഹിക്കുന്ന ആളാണെങ്കിൽ: + +- നിരവധി സംഭാവനകൾ ഉറപ്പാക്കാൻ ഞങ്ങളുടെ [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) എന്നിവയുമായി പരിചിതരാകുക. +- OpenIM-ൽ വികസന പ്രാക്ടീസുകൾ ലഭ്യമാക്കാൻ [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) അന്വേഷിക്കുക. + +### കമ്മ്യൂണിറ്റി മെയിന്റെയിനർമാർ + +ഒരു കമ്മ്യൂണിറ്റി മെയിന്റെയിനറായി: + +- സംഭാവനകൾ ഞങ്ങളുടെ ഡോക്യുമെന്റേഷനിൽ വിവരിച്ച മാനദണ്ഡങ്ങൾക്ക് അനുസൃതമാണെന്ന് ഉറപ്പാക്കുക. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) എന്നിവ പുതുക്കി വായിക്കുക. + +## ഉപയോക്താക്കൾക്ക് + +ഉപയോക്താക്കൾ പ്രത്യേകം ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM-ന്റെ ഡോക്കർ ഇമേജുകൾ ഉപയോഗിക്കാൻ പദ്ധതിയിടുന്നെങ്കിൽ ആവശ്യമാണ്. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - ലഭ്യമായ വിവിധ ഇമേജുകൾ മനസ്സിലാക്കുകയും നിങ്ങളുടെ ആർക്കിടെക്ചറിന് അനുയോജ്യമായത് എങ്ങനെ തിരഞ്ഞെടുക്കണം എന്ന് അറിയുക. diff --git a/docs/README_nl.md b/docs/README_nl.md new file mode 100644 index 000000000..755145e6f --- /dev/null +++ b/docs/README_nl.md @@ -0,0 +1,67 @@ +# OpenIM Server-documenten + +Welkom bij de OpenIM-documentatiehub! Dit centrum biedt een uitgebreide reeks handleidingen en handleidingen die zijn ontworpen om u te helpen het meeste uit uw OpenIM-ervaring te halen. + +## Inhoudsopgave + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Richtlijnen voor bijdragen en configuraties voor ontwikkelaars +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Codeerconventies, logboekbeleid en andere transformatietools + +------ + +## Draag bij + +Deze sectie biedt ontwikkelaars een gedetailleerde handleiding over hoe ze code kunnen bijdragen, hun omgeving kunnen instellen en de bijbehorende processen kunnen volgen. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regels en conventies voor het schrijven van code in OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Een handleiding voor het uitvoeren van ontwikkelingen binnen OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Richtlijnen voor kersenplukoperaties. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - De git-workflow in OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Begeleiding bij het instellen en initialiseren van OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Hoe Docker op uw machine te installeren. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Handleiding voor het opzetten van de ontwikkelomgeving op Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Richtlijnen voor het lokaal uitvoeren van bepaalde gemeenschappelijke acties. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Methoden voor het offline inzetten van OpenIM. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Handleiding voor het gebruik van protocoltools. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Tools en bibliotheken in OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Best practices en tools voor Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Best practices en tools voor scripts. + +## Conversies + +In deze sectie worden verschillende conventies en beleidsregels binnen OpenIM geïntroduceerd, waaronder code, logs, versies en meer. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Richtlijnen en methoden voor API-conversies. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Logboekbeleid en -conventies in OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedures en conventies voor CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Conventies voor code-commits in OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Directorystructuur en conventies binnen OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lijst en beschrijvingen van foutcodes. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Conventies en conversies voor Go-code. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Beheerstrategieën voor OpenIM Docker-images, verspreid over meerdere architecturen en image-opslagplaatsen. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Verdere gedetailleerde conventies over houtkap. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Naamgevings- en beheerstrategieën voor OpenIM-versies. + + +## Voor ontwikkelaars, bijdragers en communitybeheerders + +### Ontwikkelaars en bijdragers + +Als u een ontwikkelaar bent of iemand die graag een bijdrage wil leveren: + +- Maak uzelf vertrouwd met onze [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) en [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) om soepele bijdragen te garanderen. +- Duik in de [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) om de ontwikkelpraktijken in OpenIM onder de knie te krijgen. + +### Gemeenschapsbeheerders + +Als gemeenschapsbeheerder: + +- Zorg ervoor dat bijdragen in overeenstemming zijn met de normen die in onze documentatie worden beschreven. +- Controleer regelmatig het [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) en de [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) om op de hoogte te blijven. + +## Voor gebruikers + +Gebruikers moeten bijzondere aandacht besteden aan: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Noodzakelijk als u van plan bent Docker-images van OpenIM te gebruiken. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Om de verschillende beschikbare afbeeldingen te begrijpen en hoe u de juiste voor uw architectuur kiest. \ No newline at end of file diff --git a/docs/README_pl.md b/docs/README_pl.md new file mode 100644 index 000000000..9e89c9dae --- /dev/null +++ b/docs/README_pl.md @@ -0,0 +1,67 @@ +# Dokumentacja serwera OpenIM + +Witamy w centrum dokumentacji OpenIM! Centrum to zapewnia kompleksową gamę przewodników i podręczników zaprojektowanych, aby pomóc Ci w pełni wykorzystać możliwości OpenIM. + +## Spis treści + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Wskazówki dotyczące współtworzenia i konfiguracji dla programistów +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konwencje kodowania, zasady rejestrowania i inne narzędzia do transformacji + +------ + +## Wkład + +W tej sekcji deweloperzy mogą znaleźć szczegółowy przewodnik dotyczący udostępniania kodu, konfigurowania środowiska i wykonywania powiązanych procesów. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Zasady i konwencje pisania kodu w OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Poradnik dotyczący programowania w OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Wytyczne dotyczące operacji zbierania wiśni. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Przepływ pracy git w OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Wskazówki dotyczące konfigurowania i inicjowania OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Jak zainstalować Docker na swoim komputerze. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Przewodnik po konfigurowaniu środowiska programistycznego w systemie Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Wytyczne dotyczące sposobu przeprowadzania niektórych typowych działań lokalnie. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metody wdrażania OpenIM offline. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Przewodnik dotyczący korzystania z narzędzi protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Narzędzia i biblioteki w OpenIM for Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Najlepsze praktyki i narzędzia dla Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Najlepsze praktyki i narzędzia dotyczące skryptów. + +## Konwersje + +W tej sekcji przedstawiono różne konwencje i zasady w OpenIM, obejmujące kod, dzienniki, wersje i nie tylko. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Wytyczne i metody konwersji API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Zasady i konwencje rejestrowania w OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedury i konwencje dotyczące CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konwencje dotyczące zatwierdzania kodu w OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktura katalogów i konwencje w OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista i opisy kodów błędów. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konwencje i konwersje dla kodu Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie zarządzania obrazami Dockera OpenIM obejmujące wiele architektur i repozytoriów obrazów. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Dalsze szczegółowe konwencje dotyczące pozyskiwania drewna. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie nazewnictwa i zarządzania wersjami OpenIM. + + +## Dla programistów, współpracowników i opiekunów społeczności + +### Programiści i współpracownicy + +Jeśli jesteś programistą lub osobą, która chce wnieść swój wkład: + +- Zapoznaj się z naszymi [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) i [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), aby zapewnić płynną współpracę. +- Zajrzyj do [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), aby zapoznać się z praktykami programistycznymi w OpenIM. + +### Opiekunowie społeczności + +Jako opiekun społeczności: + +- Upewnij się, że wkład jest zgodny ze standardami określonymi w naszej dokumentacji. +- Regularnie przeglądaj [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) i [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), aby być na bieżąco. + +## Dla Użytkowników + +Użytkownicy powinni zwrócić szczególną uwagę na: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Niezbędne, jeśli planujesz używać obrazów Dockera OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Aby zrozumieć różne dostępne obrazy i dowiedzieć się, jak wybrać odpowiedni dla swojej architektury. \ No newline at end of file diff --git a/docs/README_pt_BR.md b/docs/README_pt_BR.md new file mode 100644 index 000000000..e28db74a6 --- /dev/null +++ b/docs/README_pt_BR.md @@ -0,0 +1,67 @@ +# Documentação do Servidor OpenIM + +Bem-vindo ao centro de documentação do OpenIM! Este centro oferece uma ampla gama de guias e manuais projetados para ajudá-lo a aproveitar ao máximo sua experiência com o OpenIM. + +## Índice de Conteúdos + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Orientações sobre contribuições e configurações para desenvolvedores +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenções de codificação, políticas de registro e outras ferramentas de transformação + +------ + +## Contrib + +Esta seção oferece aos desenvolvedores um guia detalhado sobre como contribuir com código, configurar seu ambiente e seguir os processos associados. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regras e convenções para escrever código no OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Um guia sobre como realizar o desenvolvimento dentro do OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Diretrizes sobre operações de cherry-picking. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - O fluxo de trabalho git no OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Orientações sobre configuração e inicialização do OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Como instalar o Docker em sua máquina. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guia para configurar o ambiente de desenvolvimento no Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Diretrizes sobre como realizar certas ações comuns localmente. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Métodos para implantar o OpenIM offline. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guia sobre o uso de ferramentas protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Ferramentas e bibliotecas no OpenIM para Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Melhores práticas e ferramentas para Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Melhores práticas e ferramentas para scripts. + +## Conversions + +Esta seção apresenta várias convenções e políticas dentro do OpenIM, abrangendo código, logs, versões e mais. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Diretrizes e métodos para conversões de API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Políticas e convenções de registro no OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedimentos e convenções para CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenções para commits de código no OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Estrutura de diretórios e convenções dentro do OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista e descrições de códigos de erro. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenções e conversões para código Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Estratégias de gerenciamento para imagens Docker do OpenIM, abrangendo várias arquiteturas e repositórios de imagens. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Convenções mais detalhadas sobre registro. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Estratégias de nomeação e gerenciamento para versões do OpenIM. + + +## Para Desenvolvedores, Contribuidores e Mantenedores da Comunidade + +### Desenvolvedores & Contribuidores + +Se você é um desenvolvedor ou alguém interessado em contribuir: + +- Familiarize-se com nossas [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) e [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) para garantir contribuições suaves. +- Mergulhe no [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) para se familiarizar com as práticas de desenvolvimento no OpenIM. + +### Mantenedores da Comunidade + +Como mantenedor da comunidade: + +- Garanta que as contribuições estejam alinhadas com os padrões descritos em nossa documentação. +- Reveja regularmente a [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) e [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) para se manter atualizado. + +## Para Usuários + +Os usuários devem prestar atenção especial a: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necessário se você planeja usar imagens Docker do OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Para entender as diferentes imagens disponíveis e como escolher a certa para a sua arquitetura. diff --git a/docs/README_ru.md b/docs/README_ru.md new file mode 100644 index 000000000..0e89df7c7 --- /dev/null +++ b/docs/README_ru.md @@ -0,0 +1,67 @@ +# Документация по серверу OpenIM + +Добро пожаловать в центр документации OpenIM! Этот центр предоставляет широкий спектр руководств и руководств, призванных помочь вам максимально эффективно использовать возможности OpenIM. + +## Оглавление + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Руководство по участию и настройке для разработчиков +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Соглашения по кодированию, политики ведения журналов и другие инструменты преобразования. + +------ + +## Вклад + +В этом разделе разработчикам предлагается подробное руководство о том, как добавлять код, настраивать среду и следовать соответствующим процессам. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Правила и соглашения по написанию кода в OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Руководство о том, как вести разработку в OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Руководство по сбору урожая. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Рабочий процесс git в OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Руководство по настройке и инициализации OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Как установить Docker на свой компьютер. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Руководство по настройке среды разработки в Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Рекомендации о том, как выполнять определенные общие действия на местном уровне. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Способы развертывания OpenIM в автономном режиме. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Руководство по использованию инструментов протокола. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Инструменты и библиотеки в OpenIM для Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Лучшие практики и инструменты для Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Лучшие практики и инструменты для сценариев. + +## Конверсии + +В этом разделе представлены различные соглашения и политики OpenIM, включая код, журналы, версии и многое другое. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Рекомендации и методы преобразования API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Политики и соглашения ведения журналов в OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Процедуры и соглашения для CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Соглашения о фиксации кода в OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Структура каталогов и соглашения в OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Список и описание кодов ошибок. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Соглашения и преобразования для кода Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Стратегии управления образами OpenIM Docker, охватывающими несколько архитектур и репозиториев изображений. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Дальнейшие подробные соглашения о ведении журнала. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Стратегии именования и управления версиями OpenIM. + + +## Для разработчиков, участников и сопровождающих сообщества + +### Разработчики и участники + +Если вы разработчик или кто-то хочет внести свой вклад: + +- Ознакомьтесь с нашими [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) и [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), чтобы обеспечить бесперебойную работу. +- Погрузитесь в [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), чтобы ознакомиться с методами разработки в OpenIM. + +### Сопровождающие сообщества + +Как администратор сообщества: + +- Убедитесь, что вклады соответствуют стандартам, изложенным в нашей документации. +- Регулярно просматривайте [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) и [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), чтобы оставаться в курсе событий. + +## Для пользователей + +Пользователям следует обратить особое внимание на: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Необходимо, если вы планируете использовать Docker-образы OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Чтобы понять различные доступные изображения и как выбрать подходящее для вашей архитектуры. \ No newline at end of file diff --git a/docs/README_tr.md b/docs/README_tr.md new file mode 100644 index 000000000..d2bd119e4 --- /dev/null +++ b/docs/README_tr.md @@ -0,0 +1,67 @@ +# OpenIM Sunucu Belgeleri + +OpenIM Belgeleri merkezine hoş geldiniz! Bu merkez, OpenIM deneyiminizden en iyi şekilde faydalanmanıza yardımcı olmak için tasarlanmış kapsamlı bir rehber ve kılavuzlar koleksiyonu sunar. + +## İçindekiler + +1. [Katılım](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Geliştiriciler için katkıda bulunma ve yapılandırma rehberi +2. [Dönüşümler](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodlama kuralları, günlükleme politikaları ve diğer dönüşüm araçları + +------ + +## Katılım + +Bu bölüm, geliştiricilere kod katkısında bulunma, çevrelerini kurma ve ilişkilendirilmiş süreçleri takip etme konusunda detaylı bir rehber sunar. + +- [Kod Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM'de kod yazma kuralları ve gelenekleri. +- [Geliştirme Rehberi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM içinde geliştirme nasıl yapılır konusunda bir rehber. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Cherry-pick işlemleri için yönergeler. +- [Git Çalışma Akışı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM'deki git çalışma akışı. +- [Başlangıç Yapılandırmaları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM'i kurma ve başlatma konusunda rehberlik. +- [Docker Kurulumu](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Makinenize Docker nasıl kurulur. +- [Linux Geliştirme Ortamı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux üzerinde geliştirme ortamını kurma kılavuzu. +- [Yerel İşlemler](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Yerelde belirli yaygın işlemleri nasıl gerçekleştireceğiniz hakkında yönergeler. +- [Çevrimdışı Dağıtım](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM'in çevrimdışı nasıl dağıtılacağı yöntemleri. +- [Protoc Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Protoc araçlarını kullanma rehberi. +- [Go Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go için OpenIM'deki araçlar ve kütüphaneler. +- [Makefile Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile için en iyi uygulamalar ve araçlar. +- [Betik Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Betikler için en iyi uygulamalar ve araçlar. + +## Dönüşümler + +Bu bölüm, kod, günlükler, sürümler ve daha fazlasını içeren çeşitli OpenIM içindeki kuralları ve politikaları tanıtır. + +- [API Dönüşümleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API dönüşümleri için yönergeler ve yöntemler. +- [Günlükleme Politikası](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM'deki günlükleme politikaları ve gelenekleri. +- [CI/CD İşlemleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD için prosedürler ve gelenekler. +- [Taahhüt Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM'deki kod taahhütleri için kurallar. +- [Dizin Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM içindeki dizin yapısı ve kurallar. +- [Hata Kodları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Hata kodlarının listesi ve açıklamaları. +- [Go Kod Dönüşümleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go kodu için kurallar ve dönüşümler. +- [Docker İmaj Stratejisi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker imajlarının yönetim stratejileri, birden fazla mimariyi ve imaj depolarını kapsar. +- [Günlükleme Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Günlükleme hakkında daha fazla ayrıntılı kurallar. +- [Sürüm Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM sürümleri için adlandırma ve yönetim stratejileri. + + +## Geliştiriciler, Katkıda Bulunanlar ve Topluluk Bakımı + +### Geliştiriciler & Katkıda Bulunanlar + +Eğer bir geliştirici veya katkıda bulunmaya hevesli biriyseniz: + +- Katkılarınızı düzgün bir şekilde yapmak için [Kod Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) ve [Git Çalışma Akışı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) ile tanışın. +- OpenIM'deki geliştirme uygulamalarını anlamak için [Geliştirme Rehberi'ne](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) göz atın. + +### Topluluk Bakımı + +Topluluk bakımı olarak: + +- Katkıların belirtilen standartlarla uyumlu olduğundan emin olun. +- [Günlükleme Politikası](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) ve [Hata Kodları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) sık sık gözden geçirerek güncel kalın. + +## Kullanıcılar İçin + +Kullanıcılar, özellikle dikkat etmelidir: + +- [Docker Kurulumu](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM Docker imajlarını kullanmayı planlıyorsanız gereklidir. +- [Docker İmaj Stratejisi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Mevcut farklı imajları anlamak ve mimarinize uygun olanı nasıl seçeceğinizi öğrenmek için. diff --git a/docs/README_ua.md b/docs/README_ua.md new file mode 100644 index 000000000..1eaf6b5d1 --- /dev/null +++ b/docs/README_ua.md @@ -0,0 +1,67 @@ +# OpenIM Server документ + +Ласкаво просимо до Центру документації OpenIM! Цей центр надає вичерпні посібники та посібники, розроблені, щоб допомогти вам отримати максимальну віддачу від роботи з OpenIM. + +## Зміст + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Посібник із внесків і налаштування для розробників +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Інструкції з кодування, політики журналювання та інші інструменти перетворення + +------ + +## Посібник із внесків + +Цей розділ надає розробникам докладні вказівки щодо того, як додати код, налаштувати своє середовище та дотримуватися пов’язаних процесів. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Правила та умовності для написання коду в OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Посібник з розробки в OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Ретельно підібрані інструкції. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - робочий процес git у OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Посібник із налаштування та ініціалізації OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Як встановити Docker на вашу машину. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Посібник із налаштування середовища розробки в Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Посібник із виконання деяких типових операцій локально. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Як розгорнути OpenIM офлайн. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Посібник із використання інструменту protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Інструменти та бібліотеки для Go в OpenIM. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Найкращі практики та інструменти для Makefiles. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Найкращі практики та інструменти для створення сценаріїв. + +## Методи внеску + +У цьому розділі описано різні практики та політики в OpenIM, зокрема код, журнали, версії тощо. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Рекомендації та методи перетворення API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Політика та практика журналювання в OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Процедури та практики CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Конвенції для подання коду в OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Конвенції для подання коду в OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Перелік і опис кодів помилок. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Конвенції та перетворення коду Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Стратегія керування зображеннями OpenIM Docker, що охоплює кілька архітектур і сховищ зображень. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Більш детальні умови для журналювання. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Стратегії іменування та керування для версій OpenIM. + + +## Для розробників, співавторів і супроводжувачів спільноти + +### Розробники та учасники + +Якщо ви розробник або бажаєте зробити внесок: + +- знайомі з нами [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) і [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), щоб забезпечити плавний внесок. +- зрозуміти глибше [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), освоїти практики розробки OpenIM. + +### супроводжувач спільноти + +Як супроводжувач спільноти: + +- Переконайтеся, що внески відповідають стандартам, викладеним у нашій документації. +- Регулярно перевіряйте [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) i [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), щоб бути в курсі подій. + +## Для користувачів + +Користувачам слід звернути особливу увагу на: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Це буде необхідно, якщо ви плануєте використовувати образ Docker OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Дізнайтеся про доступні зображення та про те, як вибрати правильний для вашої архітектури. diff --git a/docs/README_vi.md b/docs/README_vi.md new file mode 100644 index 000000000..55a99b8e5 --- /dev/null +++ b/docs/README_vi.md @@ -0,0 +1,67 @@ +# Tài liệu Máy chủ OpenIM + +Chào mừng bạn đến với trung tâm tài liệu OpenIM! Trung tâm này cung cấp một loạt các hướng dẫn và hướng dẫn chi tiết được thiết kế để giúp bạn tận dụng tối đa trải nghiệm OpenIM của mình. + +## Mục lục + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Hướng dẫn về đóng góp và cấu hình cho các nhà phát triển +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Quy ước mã hóa, chính sách ghi nhật ký và các công cụ chuyển đổi khác + +------ + +## Đóng góp + +Phần này cung cấp cho các nhà phát triển một hướng dẫn chi tiết về cách đóng góp mã, thiết lập môi trường của họ và tuân theo các quy trình liên quan. + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Quy tắc và quy ước viết mã trong OpenIM. +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Hướng dẫn về cách thực hiện phát triển trong OpenIM. +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Hướng dẫn về các hoạt động chọn lọc. +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Quy trình làm việc git trong OpenIM. +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Hướng dẫn về thiết lập và khởi tạo OpenIM. +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cách cài đặt Docker trên máy của bạn. +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Hướng dẫn thiết lập môi trường phát triển trên Linux. +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Hướng dẫn về cách thực hiện một số hành động phổ biến ở cấp địa phương. +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Các phương pháp triển khai OpenIM ngoại tuyến. +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Hướng dẫn sử dụng công cụ protoc. +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Công cụ và thư viện trong OpenIM cho Go. +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Thực hành tốt nhất và công cụ cho Makefile. +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Thực hành tốt nhất và công cụ cho kịch bản. + +## Chuyển đổi + +Phần này giới thiệu các quy ước và chính sách khác nhau trong OpenIM, bao gồm mã, nhật ký, phiên bản và hơn thế nữa. + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Hướng dẫn và phương pháp chuyển đổi API. +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Chính sách và quy ước ghi nhật ký trong OpenIM. +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Quy trình và quy ước cho CI/CD. +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Quy ước cho các cam kết mã trong OpenIM. +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Cấu trúc thư mục và quy ước trong OpenIM. +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Danh sách và mô tả các mã lỗi. +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Quy ước và chuyển đổi cho mã Go. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Chiến lược quản lý hình ảnh Docker của OpenIM, bao gồm nhiều kiến trúc và kho lưu trữ hình ảnh. +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Quy ước chi tiết hơn về ghi nhật ký. +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Chiến lược đặt tên và quản lý phiên bản OpenIM. + + +## Dành cho Nhà phát triển, Người đóng góp và Người duy trì Cộng đồng + +### Nhà phát triển & Người đóng góp + +Nếu bạn là nhà phát triển hoặc ai đó muốn đóng góp: + +- Làm quen với [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) và [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) của chúng tôi để đảm bảo đóng góp trôi chảy. +- Tìm hiểu [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) để nắm bắt các thực hành phát triển trong OpenIM. + +### Người duy trì Cộng đồng + +Là người duy trì cộng đồng: + +- Đảm bảo rằng các đóng góp phù hợp với các tiêu chuẩn được nêu trong tài liệu của chúng tôi. +- Thường xuyên xem lại [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) và [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) để cập nhật thông tin. + +## Dành cho Người dùng + +Người dùng nên chú ý đặc biệt đến: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cần thiết nếu bạn dự định sử dụng hình ảnh Docker của OpenIM. +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Để hiểu các hình ảnh khác nhau có sẵn và cách chọn hình ảnh phù hợp cho kiến trúc của bạn. diff --git a/docs/README_zh_CN.md b/docs/README_zh_CN.md new file mode 100644 index 000000000..413d5dfa1 --- /dev/null +++ b/docs/README_zh_CN.md @@ -0,0 +1,67 @@ +# OpenIM Server 文档 + +欢迎来到 OpenIM 文档中心!本中心提供全面的指南和手册,旨在帮助您最大限度地利用 OpenIM 体验。 + +## 目录 + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 为开发者提供的贡献指南和配置 +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 编码规范、日志策略和其他转换工具 + +------ + +## 贡献指南 + +本节为开发人员提供了如何贡献代码、设置环境以及遵循相关流程的详细指南。 + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM 中编写代码的规则和惯例。 +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - 关于如何在 OpenIM 内进行开发的指南。 +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 精挑细选的操作指南。 +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM 中的 git 工作流程。 +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - 设置和初始化 OpenIM 的指南。 +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如何在您的机器上安装 Docker。 +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - 在 Linux 上设置开发环境的指南。 +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 关于如何在本地执行某些常见操作的指南。 +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 离线部署 OpenIM 的方法。 +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 使用 protoc 工具的指南。 +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM 中 Go 的工具和库。 +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile 的最佳实践和工具。 +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 脚本的最佳实践和工具。 + +## 贡献方法 + +本节介绍 OpenIM 内的各种惯例和政策,包括代码、日志、版本等。 + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 转换的指南和方法。 +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM 中的日志策略和惯例。 +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 的程序和惯例。 +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM 中代码提交的惯例。 +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 内的目录结构和惯例。 +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 错误代码的列表和描述。 +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 代码的惯例和转换。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker 镜像的管理策略,涵盖多个架构和镜像仓库。 +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 有关日志的更详细的惯例。 +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 版本的命名和管理策略。 + + +## 对于开发者、贡献者和社区维护者 + +### 开发者和贡献者 + +如果您是一名开发者或热衷于贡献: + +- 熟悉我们的 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 和 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md),以确保顺利贡献。 +- 深入了解 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md),掌握 OpenIM 的开发实践。 + +### 社区维护者 + +作为社区维护者: + +- 确保贡献符合我们文档中概述的标准。 +- 定期查看 [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 和 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md),以保持最新状态。 + +## 对于用户 + +用户应特别注意: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如果您计划使用 OpenIM 的 Docker 镜像,那么这个将会是必须的。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 了解可用的镜像以及如何为您的架构选择正确的镜像。 diff --git a/docs/README_zh_TW.md b/docs/README_zh_TW.md new file mode 100644 index 000000000..8ca00ba17 --- /dev/null +++ b/docs/README_zh_TW.md @@ -0,0 +1,67 @@ +# OpenIM 伺服器文檔 + +歡迎來到 OpenIM 文件中心! 該中心提供全面的指南和手冊,旨在幫助您充分利用 OpenIM 體驗。 + +## 目錄 + +1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 開發人員貢獻和配置指南 +2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 編碼約定、日誌記錄策略和其他轉換工具 + +------ + +## 貢獻 + +本節為開發人員提供了有關如何貢獻程式碼、設定環境以及遵循相關流程的詳細指南。 + +- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - 在 OpenIM 中編寫程式碼的規則和約定。 +- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - 有關如何在 OpenIM 中進行開發的指南。 +- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 精挑細選操作指南。 +- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM 中的 git 工作流程。 +- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - 設定和初始化 OpenIM 的指南。 +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如何在您的電腦上安裝 Docker。 +- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux 上的開發環境設定指南。 +- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 關於如何在當地進行某些共同行動的指南。 +- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 離線部署OpenIM的方法。 +- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 協議工具使用指南。 +- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM 在 Go 中的工具和函式庫。 +- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile 的最佳實務和工具。 +- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 腳本的最佳實踐和工具。 + +## 轉換 + +本節介紹 OpenIM 中的各種約定和策略,包括程式碼、日誌、版本等。 + +- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 轉換的指南和方法。 +- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM 中的日誌記錄策略和約定。 +- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 的程序和約定。 +- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM 中程式碼提交的約定。 +- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 中的目錄結構和約定。 +- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 錯誤代碼的清單和描述。 +- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 程式碼的約定和轉換。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker 映像的管理策略,跨越多種架構和映像儲存庫。 +- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 有關日誌記錄的更詳細約定。 +- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 版本的命名與管理策略。 + + +## 對於開發者、貢獻者和社區維護者 + +### 開發者和貢獻者 + +如果您是開發人員或熱衷於做出貢獻的人: + +- 熟悉我們的 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 和 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) 以確保順利貢獻。 +- 深入閱讀 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) ,掌握 OpenIM 的開發實務。 + +### 社區維護者 + +作為社區維護者: + +- 確保貢獻符合我們文件中概述的標準。 +- 定期查看 [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 和 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) 以保持更新。 + +## 對於用戶 + +使用者應特別注意: + +- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如果您打算使用 OpenIM 的 Docker 映像,則這是必要的。 +- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 了解可用的不同影像以及如何為您的架構選擇正確的影像。 \ No newline at end of file From 551781a0ee4ddbfbe9af64b7431866f2d0ee69b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=9B=E8=9B=8B=E7=99=BD?= Date: Thu, 1 Feb 2024 10:10:38 +0800 Subject: [PATCH 03/74] fix document parameter errors (#1848) Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- docs/contrib/environment.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/contrib/environment.md b/docs/contrib/environment.md index e18246afa..dc11a3c7b 100644 --- a/docs/contrib/environment.md +++ b/docs/contrib/environment.md @@ -305,6 +305,7 @@ Feel free to explore the MinIO documentation for more advanced configurations an This section involves setting up MongoDB, including its port, address, and credentials. + | Parameter | Example Value | Description | | -------------- | -------------- | ----------------------- | | MONGO_PORT | "27017" | Port used by MongoDB. | From 55ca661d138e548dbd94ad2e557f11482d286ad1 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:11:22 +0800 Subject: [PATCH 04/74] Bug: replace the component check func by tools pkg (#1846) * fix: del the manager config and manger init statement * fix: fix the Manger judge condition * fix: fix revokeMsg error * fix: find erors * fix: find error * fix: fix the AdminAccount error * fix: del the debug statement * fix: fix the component check func * fix: fix the get zkAddress error * fix: fix the kafka client close error * fix: add env in minio connected * fix: del the minio env * fix: fix the go.mod tools version * fix: del get env in minio conneted --- go.mod | 12 +- go.sum | 25 ++- pkg/rpcclient/third.go | 4 +- tools/component/component.go | 300 ++++++++++++----------------------- 4 files changed, 123 insertions(+), 218 deletions(-) diff --git a/go.mod b/go.mod index c709e9ba4..a5fecb5ee 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.48 - github.com/OpenIMSDK/tools v0.0.29 + github.com/OpenIMSDK/tools v0.0.31 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 @@ -31,7 +31,7 @@ require ( gopkg.in/yaml.v3 v3.0.1 ) -require github.com/google/uuid v1.3.1 +require github.com/google/uuid v1.5.0 require ( github.com/IBM/sarama v1.41.3 @@ -94,8 +94,8 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect - github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/compress v1.17.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/lithammer/shortuuid v3.0.0+incompatible // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect @@ -129,7 +129,7 @@ require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.17.0 // indirect + golang.org/x/net v0.19.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect @@ -141,7 +141,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - gorm.io/gorm v1.23.8 // indirect + gorm.io/gorm v1.25.4 // indirect stathat.com/c/consistent v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index b7b40632f..fbd9366c3 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.29 h1:NS4PEwYl9sX3SWsMjDOLVxMo3LcTWREMr+2cjzWjcqc= -github.com/OpenIMSDK/tools v0.0.29/go.mod h1:eg+q4A34Qmu73xkY0mt37FHGMCMfC6CtmOnm0kFEGFI= +github.com/OpenIMSDK/tools v0.0.31 h1:fSrhcPTvHEMTSyrJZDupe730mL4nuhvSOUP/BaZiHaY= +github.com/OpenIMSDK/tools v0.0.31/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= @@ -152,8 +152,8 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= @@ -194,7 +194,6 @@ github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= -github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= @@ -205,12 +204,12 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= +github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -402,8 +401,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= @@ -531,8 +530,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE= -gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= +gorm.io/gorm v1.25.4 h1:iyNd8fNAe8W9dvtlgeRI5zSVZPsq3OpcTu37cYcpCmw= +gorm.io/gorm v1.25.4/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index 48a537112..0d5708fc8 100755 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -16,12 +16,10 @@ package rpcclient import ( "context" - "net/url" - "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "google.golang.org/grpc" + "net/url" "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" diff --git a/tools/component/component.go b/tools/component/component.go index 220b845ee..4200b46f5 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -15,44 +15,26 @@ package main import ( - "context" "errors" "flag" "fmt" - "net" - "net/url" "os" "strings" "time" - "github.com/IBM/sarama" + "github.com/OpenIMSDK/tools/component" "github.com/OpenIMSDK/tools/errs" - "github.com/go-zookeeper/zk" - "go.mongodb.org/mongo-driver/mongo" - "go.mongodb.org/mongo-driver/mongo/options" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/redis/go-redis/v9" "gopkg.in/yaml.v3" ) const ( // defaultCfgPath is the default path of the configuration file. - defaultCfgPath = "../../../../../config/config.yaml" - minioHealthCheckDuration = 1 - maxRetry = 300 - componentStartErrCode = 6000 - configErrCode = 6001 - mongoConnTimeout = 30 * time.Second -) - -const ( - colorRed = 31 - colorGreen = 32 - colorYellow = 33 + defaultCfgPath = "../../../../../config/config.yaml" + maxRetry = 300 + componentStartErrCode = 6000 + configErrCode = 6001 ) var ( @@ -103,16 +85,16 @@ func main() { for _, check := range checks { str, err := check.function() if err != nil { - errorPrint(fmt.Sprintf("Starting %s failed, %v", check.name, err)) + component.ErrorPrint(fmt.Sprintf("Starting %s failed, %v", check.name, err)) allSuccess = false break } else { - successPrint(fmt.Sprintf("%s connected successfully, %s", check.name, str)) + component.SuccessPrint(fmt.Sprintf("%s connected successfully, %s", check.name, str)) } } if allSuccess { - successPrint("All components started successfully!") + component.SuccessPrint("All components started successfully!") return } @@ -120,19 +102,6 @@ func main() { os.Exit(1) } -func exactIP(urll string) string { - u, _ := url.Parse(urll) - host, _, err := net.SplitHostPort(u.Host) - if err != nil { - host = u.Host - } - if strings.HasSuffix(host, ":") { - host = host[0 : len(host)-1] - } - - return host -} - // Helper function to get environment variable or default value func getEnv(key, fallback string) string { if value, exists := os.LookupEnv(key); exists { @@ -143,45 +112,23 @@ func getEnv(key, fallback string) string { // checkMongo checks the MongoDB connection without retries func checkMongo() (string, error) { - uri := getEnv("MONGO_URI", buildMongoURI()) - - ctx, cancel := context.WithTimeout(context.Background(), mongoConnTimeout) - defer cancel() - - str := "ths addr is:" + strings.Join(config.Config.Mongo.Address, ",") - - client, err := mongo.Connect(ctx, options.Client().ApplyURI(uri)) - if err != nil { - return "", errs.Wrap(errStr(err, str)) + mongo := &component.Mongo{ + Address: config.Config.Mongo.Address, + Database: config.Config.Mongo.Database, + Username: config.Config.Mongo.Username, + Password: config.Config.Mongo.Password, + MaxPoolSize: config.Config.Mongo.MaxPoolSize, } - defer client.Disconnect(context.Background()) - - ctx, cancel = context.WithTimeout(context.Background(), mongoConnTimeout) - defer cancel() - - if err = client.Ping(ctx, nil); err != nil { - return "", errs.Wrap(errStr(err, str)) + uri, uriExist := os.LookupEnv("MONGO_URI") + if uriExist { + mongo.URL = uri } - return str, nil -} - -// buildMongoURI constructs the MongoDB URI using configuration settings -func buildMongoURI() string { - // Fallback to config if environment variables are not set - username := config.Config.Mongo.Username - password := config.Config.Mongo.Password - database := config.Config.Mongo.Database - maxPoolSize := config.Config.Mongo.MaxPoolSize - - mongodbHosts := strings.Join(config.Config.Mongo.Address, ",") - - if username != "" && password != "" { - return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%d", - username, password, mongodbHosts, database, maxPoolSize) + str, err := component.CheckMongo(mongo) + if err != nil { + return "", err } - return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%d", - mongodbHosts, database, maxPoolSize) + return str, nil } // checkMinio checks the MinIO connection @@ -191,52 +138,24 @@ func checkMinio() (string, error) { return "", nil } - // Prioritize environment variables - endpoint := getEnv("MINIO_ENDPOINT", config.Config.Object.Minio.Endpoint) - accessKeyID := getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID) - secretAccessKey := getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey) - useSSL := getEnv("MINIO_USE_SSL", "false") // Assuming SSL is not used by default - - if endpoint == "" || accessKeyID == "" || secretAccessKey == "" { - return "", ErrConfig.Wrap("MinIO configuration missing") - } - - // Parse endpoint URL to determine if SSL is enabled - u, err := url.Parse(endpoint) + endpoint, err := getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Config.Object.Minio.Endpoint) if err != nil { - str := "the endpoint is:" + endpoint - return "", errs.Wrap(errStr(err, str)) - } - secure := u.Scheme == "https" || useSSL == "true" - - // Initialize MinIO client - minioClient, err := minio.New(u.Host, &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: secure, - }) - str := "ths addr is:" + u.Host - if err != nil { - strs := fmt.Sprintf("%v;host:%s,accessKeyID:%s,secretAccessKey:%s,Secure:%v", err, u.Host, accessKeyID, secretAccessKey, secure) - return "", errs.Wrap(err, strs) + return "", err } - // Perform health check - cancel, err := minioClient.HealthCheck(time.Duration(minioHealthCheckDuration) * time.Second) - if err != nil { - return "", errs.Wrap(errStr(err, str)) + minio := &component.Minio{ + ApiURL: config.Config.Object.ApiURL, + Endpoint: endpoint, + AccessKeyID: getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID), + SecretAccessKey: getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey), + SignEndpoint: config.Config.Object.Minio.SignEndpoint, + UseSSL: getEnv("MINIO_USE_SSL", "false"), } - defer cancel() - if minioClient.IsOffline() { - str := fmt.Sprintf("Minio server is offline;%s", str) - return "", ErrComponentStart.Wrap(str) - } - - // Check for localhost in API URL and Minio SignEndpoint - if exactIP(config.Config.Object.ApiURL) == "127.0.0.1" || exactIP(config.Config.Object.Minio.SignEndpoint) == "127.0.0.1" { - return "", ErrConfig.Wrap("apiURL or Minio SignEndpoint endpoint contain 127.0.0.1") + str, err := component.CheckMinio(minio) + if err != nil { + return "", err } - return str, nil } @@ -247,76 +166,48 @@ func checkRedis() (string, error) { username := getEnv("REDIS_USERNAME", config.Config.Redis.Username) password := getEnv("REDIS_PASSWORD", config.Config.Redis.Password) - // Split address to handle multiple addresses for cluster setup - redisAddresses := strings.Split(address, ",") - - var redisClient redis.UniversalClient - if len(redisAddresses) > 1 { - // Use cluster client for multiple addresses - redisClient = redis.NewClusterClient(&redis.ClusterOptions{ - Addrs: redisAddresses, - Username: username, - Password: password, - }) - } else { - // Use regular client for single address - redisClient = redis.NewClient(&redis.Options{ - Addr: redisAddresses[0], - Username: username, - Password: password, - }) + redis := &component.Redis{ + Address: strings.Split(address, ","), + Username: username, + Password: password, } - defer redisClient.Close() - // Ping Redis to check connectivity - _, err := redisClient.Ping(context.Background()).Result() - str := "the addr is:" + strings.Join(redisAddresses, ",") + addresses, err := getAddress("REDIS_ADDRESS", "REDIS_PORT", config.Config.Redis.Address) if err != nil { - return "", errs.Wrap(errStr(err, str)) + return "", err } + redis.Address = addresses + str, err := component.CheckRedis(redis) + if err != nil { + return "", err + } return str, nil } // checkZookeeper checks the Zookeeper connection func checkZookeeper() (string, error) { // Prioritize environment variables - schema := getEnv("ZOOKEEPER_SCHEMA", "digest") + address := getEnv("ZOOKEEPER_ADDRESS", strings.Join(config.Config.Zookeeper.ZkAddr, ",")) - username := getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username) - password := getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password) - // Split addresses to handle multiple Zookeeper nodes - zookeeperAddresses := strings.Split(address, ",") + zk := &component.Zookeeper{ + Schema: getEnv("ZOOKEEPER_SCHEMA", "digest"), + ZkAddr: strings.Split(address, ","), + Username: getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username), + Password: getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password), + } - // Connect to Zookeeper - str := "the addr is:" + address - c, eventChan, err := zk.Connect(zookeeperAddresses, time.Second) // Adjust the timeout as necessary + addresses, err := getAddress("ZOOKEEPER_ADDRESS", "ZOOKEEPER_PORT", config.Config.Zookeeper.ZkAddr) if err != nil { - return "", errs.Wrap(errStr(err, str)) - } - timeout := time.After(5 * time.Second) - for { - select { - case event := <-eventChan: - if event.State == zk.StateConnected { - fmt.Println("Connected to Zookeeper") - goto Connected - } - case <-timeout: - return "", errs.Wrap(errors.New("timeout waiting for Zookeeper connection"), "Zookeeper Addr: "+strings.Join(config.Config.Zookeeper.ZkAddr, " ")) - } + return "", nil } -Connected: - defer c.Close() + zk.ZkAddr = addresses - // Set authentication if username and password are provided - if username != "" && password != "" { - if err := c.AddAuth(schema, []byte(username+":"+password)); err != nil { - return "", errs.Wrap(errStr(err, str)) - } + str, err := component.CheckZookeeper(zk) + if err != nil { + return "", err } - return str, nil } @@ -327,24 +218,21 @@ func checkKafka() (string, error) { password := getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password) address := getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ",")) - // Split addresses to handle multiple Kafka brokers - kafkaAddresses := strings.Split(address, ",") + kafka := &component.Kafka{ + Username: username, + Password: password, + Addr: strings.Split(address, ","), + } - // Configure Kafka client - cfg := sarama.NewConfig() - if username != "" && password != "" { - cfg.Net.SASL.Enable = true - cfg.Net.SASL.User = username - cfg.Net.SASL.Password = password + addresses, err := getAddress("KAFKA_ADDRESS", "KAFKA_PORT", config.Config.Kafka.Addr) + if err != nil { + return "", nil } - // Additional Kafka setup (e.g., TLS configuration) can be added here - // kafka.SetupTLSConfig(cfg) + kafka.Addr = addresses - // Create Kafka client - str := "the addr is:" + address - kafkaClient, err := sarama.NewClient(kafkaAddresses, cfg) + str, kafkaClient, err := component.CheckKafka(kafka) if err != nil { - return "", errs.Wrap(errStr(err, str)) + return "", err } defer kafkaClient.Close() @@ -379,22 +267,42 @@ func isTopicPresent(topic string, topics []string) bool { return false } -func colorPrint(colorCode int, format string, a ...interface{}) { - fmt.Printf("\x1b[%dm%s\x1b[0m\n", colorCode, fmt.Sprintf(format, a...)) -} - -func errorPrint(s string) { - colorPrint(colorRed, "%v", s) -} - -func successPrint(s string) { - colorPrint(colorGreen, "%v", s) -} +func getAddress(key1, key2 string, fallback []string) ([]string, error) { + address, addrExist := os.LookupEnv(key1) + port, portExist := os.LookupEnv(key2) -func warningPrint(s string) { - colorPrint(colorYellow, "Warning: But %v", s) + if addrExist && portExist { + addresses := strings.Split(address, ",") + for i, addr := range addresses { + addresses[i] = addr + ":" + port + } + return addresses, nil + } else if !addrExist && portExist { + result := make([]string, len(config.Config.Redis.Address)) + for i, addr := range config.Config.Redis.Address { + add := strings.Split(addr, ":") + result[i] = add[0] + ":" + port + } + return result, nil + } else if addrExist && !portExist { + return nil, errs.Wrap(errors.New("the ZOOKEEPER_PORT of minio is empty")) + } + return fallback, nil } -func errStr(err error, str string) error { - return fmt.Errorf("%v;%s", err, str) -} +func getMinioAddr(key1, key2, key3, fallback string) (string, error) { + // Prioritize environment variables + endpoint := getEnv(key1, fallback) + address, addressExist := os.LookupEnv(key2) + port, portExist := os.LookupEnv(key3) + if portExist && addressExist { + endpoint = "http://" + address + ":" + port + } else if !portExist && addressExist { + return "", errs.Wrap(errors.New("the MINIO_PORT of minio is empty")) + } else if portExist && !addressExist { + arr := strings.Split(config.Config.Object.Minio.Endpoint, ":") + arr[2] = port + endpoint = strings.Join(arr, ":") + } + return endpoint, nil +} \ No newline at end of file From c55e03dc70b0fbe9091e9c8d4123546348ecb30f Mon Sep 17 00:00:00 2001 From: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Fri, 2 Feb 2024 10:11:13 +0800 Subject: [PATCH 05/74] fix: process add errors wrap. (#1862) * fix: process add errors wrap. * fix: process add errors wrap. --- cmd/openim-api/main.go | 25 ++-------- internal/msgtransfer/init.go | 49 +++++++++++-------- .../msgtransfer/online_history_msg_handler.go | 7 +-- .../online_msg_to_mongo_handler.go | 21 +++++--- internal/push/consumer_init.go | 17 ++++--- internal/push/push_handler.go | 10 ++-- internal/push/push_rpc_server.go | 5 +- internal/rpc/msg/server.go | 5 +- internal/tools/cron_task.go | 14 +++--- internal/tools/msg.go | 5 +- pkg/common/db/cache/init_redis.go | 4 +- pkg/common/db/controller/msg.go | 27 +++++++--- pkg/common/db/mgo/conversation.go | 3 +- pkg/common/db/mgo/group.go | 3 +- pkg/common/db/mgo/group_member.go | 3 +- pkg/common/db/mgo/group_request.go | 3 +- pkg/common/db/mgo/user.go | 2 +- pkg/common/db/unrelation/mongo.go | 8 ++- pkg/common/kafka/consumer_group.go | 16 +++--- pkg/common/kafka/producer.go | 9 ++-- pkg/common/startrpc/start.go | 16 +++--- 21 files changed, 142 insertions(+), 110 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index f0b62e31f..59e0b7f9e 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -17,6 +17,7 @@ package main import ( "context" "fmt" + "github.com/OpenIMSDK/tools/errs" "net" "net/http" _ "net/http/pprof" @@ -28,8 +29,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -44,55 +43,42 @@ func main() { apiCmd.AddPortFlag() apiCmd.AddApi(run) if err := apiCmd.Execute(); err != nil { - log.ZError(context.Background(), "API command execution failed", err) panic(err.Error()) } } func run(port int, proPort int) error { - log.ZInfo(context.Background(), "Openim api port:", "port", port, "proPort", proPort) - if port == 0 || proPort == 0 { err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort) - log.ZError(context.Background(), err, nil) - return fmt.Errorf(err) + return errs.Wrap(fmt.Errorf(err)) } - rdb, err := cache.NewRedis() if err != nil { - log.ZError(context.Background(), "Failed to initialize Redis", err) return err } - log.ZInfo(context.Background(), "api start init discov client") var client discoveryregistry.SvcDiscoveryRegistry // Determine whether zk is passed according to whether it is a clustered deployment client, err = kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) if err != nil { - log.ZError(context.Background(), "Failed to initialize discovery register", err) - return err + return errs.Wrap(err, "register discovery err") } if err = client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { - log.ZError(context.Background(), "Failed to create RPC root nodes", err) - return err + return errs.Wrap(err, "create rpc root nodes error") } - log.ZInfo(context.Background(), "api register public config to discov") if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.Config.EncodeConfig()); err != nil { - log.ZError(context.Background(), "Failed to register public config to discov", err) return err } - log.ZInfo(context.Background(), "api register public config to discov success") router := api.NewGinRouter(client, rdb) if config.Config.Prometheus.Enable { p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api")) p.SetListenAddress(fmt.Sprintf(":%d", proPort)) p.Use(router) } - log.ZInfo(context.Background(), "api init router success") var address string if config.Config.Api.ListenIP != "" { @@ -100,13 +86,11 @@ func run(port int, proPort int) error { } else { address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) } - log.ZInfo(context.Background(), "start api server", "address", address, "OpenIM version", config.Version) server := http.Server{Addr: address, Handler: router} go func() { err = server.ListenAndServe() if err != nil && err != http.ErrServerClosed { - log.ZError(context.Background(), "api run failed", err, "address", address) os.Exit(1) } }() @@ -120,7 +104,6 @@ func run(port int, proPort int) error { // graceful shutdown operation. if err := server.Shutdown(ctx); err != nil { - log.ZError(context.Background(), "failed to api-server shutdown", err) return err } diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 7d692662d..969761a9d 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -15,8 +15,10 @@ package msgtransfer import ( + "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "log" "net/http" "sync" @@ -69,40 +71,47 @@ func StartTransfer(prometheusPort int) error { client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) msgModel := cache.NewMsgCacheModel(rdb) msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) - msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, msgModel) + msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel) + if err != nil { + return err + } conversationRpcClient := rpcclient.NewConversationRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgTransfer := NewMsgTransfer(msgDatabase, &conversationRpcClient, &groupRpcClient) + msgTransfer, err := NewMsgTransfer(msgDatabase, &conversationRpcClient, &groupRpcClient) + if err != nil { + return err + } return msgTransfer.Start(prometheusPort) } -func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) *MsgTransfer { - return &MsgTransfer{ - historyCH: NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient), - historyMongoCH: NewOnlineHistoryMongoConsumerHandler(msgDatabase), +func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) { + historyCH, err := NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient) + if err != nil { + return nil, err } + historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(msgDatabase) + if err != nil { + return nil, err + } + + return &MsgTransfer{ + historyCH: historyCH, + historyMongoCH: historyMongoCH, + }, nil } func (m *MsgTransfer) Start(prometheusPort int) error { + ctx := context.Background() var wg sync.WaitGroup wg.Add(1) fmt.Println("start msg transfer", "prometheusPort:", prometheusPort) if prometheusPort <= 0 { - return errors.New("prometheusPort not correct") - } - if config.Config.ChatPersistenceMysql { - // go m.persistentCH.persistentConsumerGroup.RegisterHandleAndConsumer(m.persistentCH) - } else { - fmt.Println("msg transfer not start mysql consumer") + return errs.Wrap(errors.New("prometheusPort not correct")) } - go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyCH) - go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.historyMongoCH) - // go m.modifyCH.modifyMsgConsumerGroup.RegisterHandleAndConsumer(m.modifyCH) - /*err := prome.StartPrometheusSrv(prometheusPort) - if err != nil { - return err - }*/ - //////////////////////////// + + go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyCH) + go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyMongoCH) + if config.Config.Prometheus.Enable { reg := prometheus.NewRegistry() reg.MustRegister( diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 127cede71..35af330c9 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -87,7 +87,7 @@ func NewOnlineHistoryRedisConsumerHandler( database controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, -) *OnlineHistoryRedisConsumerHandler { +) (*OnlineHistoryRedisConsumerHandler, error) { var och OnlineHistoryRedisConsumerHandler och.msgDatabase = database och.msgDistributionCh = make(chan Cmd2Value) // no buffer channel @@ -98,14 +98,15 @@ func NewOnlineHistoryRedisConsumerHandler( } och.conversationRpcClient = conversationRpcClient och.groupRpcClient = groupRpcClient - och.historyConsumerGroup = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ + var err error + och.historyConsumerGroup, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, }, []string{config.Config.Kafka.LatestMsgToRedis.Topic}, config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis) // statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d // second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) - return &och + return &och, err } func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) { diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index 8ef15fe72..6e6c4c819 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -34,16 +34,21 @@ type OnlineHistoryMongoConsumerHandler struct { msgDatabase controller.CommonMsgDatabase } -func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) *OnlineHistoryMongoConsumerHandler { +func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) { + historyConsumerGroup, err := kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ + KafkaVersion: sarama.V2_0_0_0, + OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, + }, []string{config.Config.Kafka.MsgToMongo.Topic}, + config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo) + if err != nil { + return nil, err + } + mc := &OnlineHistoryMongoConsumerHandler{ - historyConsumerGroup: kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ - KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.MsgToMongo.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo), - msgDatabase: database, + historyConsumerGroup: historyConsumerGroup, + msgDatabase: database, } - return mc + return mc, nil } func (mc *OnlineHistoryMongoConsumerHandler) handleChatWs2Mongo( diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index b72c32bb1..ceab86165 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -14,19 +14,24 @@ package push +import "context" + type Consumer struct { pushCh ConsumerHandler successCount uint64 } -func NewConsumer(pusher *Pusher) *Consumer { - return &Consumer{ - pushCh: *NewConsumerHandler(pusher), +func NewConsumer(pusher *Pusher) (*Consumer, error) { + c, err := NewConsumerHandler(pusher) + if err != nil { + return nil, err } + return &Consumer{ + pushCh: *c, + }, nil } func (c *Consumer) Start() { - // statistics.NewStatistics(&c.successCount, config.Config.ModuleName.PushName, fmt.Sprintf("%d second push to - // msg_gateway count", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) - go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(&c.pushCh) + + go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh) } diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index c91206ecc..19d42ebb9 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -35,15 +35,19 @@ type ConsumerHandler struct { pusher *Pusher } -func NewConsumerHandler(pusher *Pusher) *ConsumerHandler { +func NewConsumerHandler(pusher *Pusher) (*ConsumerHandler, error) { var consumerHandler ConsumerHandler consumerHandler.pusher = pusher - consumerHandler.pushConsumerGroup = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ + var err error + consumerHandler.pushConsumerGroup, err = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, }, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToPush) - return &consumerHandler + if err != nil { + return nil, err + } + return &consumerHandler, nil } func (c *ConsumerHandler) handleMs2PsChat(ctx context.Context, msg []byte) { diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 188ddc0e1..c1226ce6b 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -66,9 +66,12 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pusher: pusher, }) }() + consumer, err := NewConsumer(pusher) + if err != nil { + return err + } go func() { defer wg.Done() - consumer := NewConsumer(pusher) consumer.Start() }() wg.Wait() diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 88be287fd..fe1baa453 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -80,7 +80,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e userRpcClient := rpcclient.NewUserRpcClient(client) groupRpcClient := rpcclient.NewGroupRpcClient(client) friendRpcClient := rpcclient.NewFriendRpcClient(client) - msgDatabase := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) + msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) + if err != nil { + return err + } s := &msgServer{ Conversation: &conversationClient, User: &userRpcClient, diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index e22504bbb..cf2068d8e 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -17,6 +17,7 @@ package tools import ( "context" "fmt" + "github.com/OpenIMSDK/tools/errs" "os" "os/signal" "syscall" @@ -25,14 +26,13 @@ import ( "github.com/redis/go-redis/v9" "github.com/robfig/cron/v3" - "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) func StartTask() error { fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime) + msgTool, err := InitMsgTool() if err != nil { return err @@ -47,18 +47,16 @@ func StartTask() error { // register cron tasks var crontab = cron.New() - log.ZInfo(context.Background(), "start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime) + fmt.Println("start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime) _, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) if err != nil { - log.ZError(context.Background(), "start allConversationClearMsgAndFixSeq cron failed", err) - panic(err) + return errs.Wrap(err) } - log.ZInfo(context.Background(), "start msgDestruct cron task", "cron config", config.Config.MsgDestructTime) + fmt.Println("start msgDestruct cron task", "cron config", config.Config.MsgDestructTime) _, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) if err != nil { - log.ZError(context.Background(), "start conversationsDestructMsgs cron failed", err) - panic(err) + return errs.Wrap(err) } // start crontab diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 30006670e..1ec1e03a2 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -84,7 +84,10 @@ func InitMsgTool() (*MsgTool, error) { if err != nil { return nil, err } - msgDatabase := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase()) + msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase()) + if err != nil { + return nil, err + } userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase()) ctxTx := tx.NewMongo(mongo.GetClient()) userDatabase := controller.NewUserDatabase( diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index 1308e9649..c593089e6 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -49,7 +49,7 @@ func NewRedis() (redis.UniversalClient, error) { overrideConfigFromEnv() if len(config.Config.Redis.Address) == 0 { - return nil, errors.New("redis address is empty") + return nil, errs.Wrap(errors.New("redis address is empty")) } specialerror.AddReplace(redis.Nil, errs.ErrRecordNotFound) var rdb redis.UniversalClient @@ -77,7 +77,7 @@ func NewRedis() (redis.UniversalClient, error) { defer cancel() err = rdb.Ping(ctx).Err() if err != nil { - return nil, fmt.Errorf("redis ping %w", err) + return nil, errs.Wrap(fmt.Errorf("redis ping %w", err)) } redisClient = rdb diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index dfff5c61d..b841a7d31 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -126,21 +126,32 @@ type CommonMsgDatabase interface { ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) } -func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel) CommonMsgDatabase { +func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel) (CommonMsgDatabase, error) { + producerToRedis, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic) + if err != nil { + return nil, err + } + producerToMongo, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic) + if err != nil { + return nil, err + } + producerToPush, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic) + if err != nil { + return nil, err + } return &commonMsgDatabase{ msgDocDatabase: msgDocModel, cache: cacheModel, - producer: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic), - producerToMongo: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic), - producerToPush: kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic), - } + producer: producerToRedis, + producerToMongo: producerToMongo, + producerToPush: producerToPush, + }, nil } -func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) CommonMsgDatabase { +func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) (CommonMsgDatabase, error) { cacheModel := cache.NewMsgCacheModel(rdb) msgDocModel := unrelation.NewMsgMongoDriver(database) - CommonMsgDatabase := NewCommonMsgDatabase(msgDocModel, cacheModel) - return CommonMsgDatabase + return NewCommonMsgDatabase(msgDocModel, cacheModel) } type commonMsgDatabase struct { diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index 1614cfec5..0b8d597dc 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "time" "github.com/OpenIMSDK/protocol/constant" @@ -38,7 +39,7 @@ func NewConversationMongo(db *mongo.Database) (*ConversationMgo, error) { Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &ConversationMgo{coll: coll}, nil } diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go index a9c6d1eb8..9a4c660a6 100644 --- a/pkg/common/db/mgo/group.go +++ b/pkg/common/db/mgo/group.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "time" "github.com/OpenIMSDK/tools/mgoutil" @@ -36,7 +37,7 @@ func NewGroupMongo(db *mongo.Database) (relation.GroupModelInterface, error) { Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &GroupMgo{coll: coll}, nil } diff --git a/pkg/common/db/mgo/group_member.go b/pkg/common/db/mgo/group_member.go index 8e3dd1efa..fce79830e 100644 --- a/pkg/common/db/mgo/group_member.go +++ b/pkg/common/db/mgo/group_member.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/mgoutil" @@ -37,7 +38,7 @@ func NewGroupMember(db *mongo.Database) (relation.GroupMemberModelInterface, err Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &GroupMemberMgo{coll: coll}, nil } diff --git a/pkg/common/db/mgo/group_request.go b/pkg/common/db/mgo/group_request.go index cb04d2308..dce0878ee 100644 --- a/pkg/common/db/mgo/group_request.go +++ b/pkg/common/db/mgo/group_request.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" @@ -36,7 +37,7 @@ func NewGroupRequestMgo(db *mongo.Database) (relation.GroupRequestModelInterface Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &GroupRequestMgo{coll: coll}, nil } diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index 34a25ed08..2797bc53f 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -40,7 +40,7 @@ func NewUserMongo(db *mongo.Database) (relation.UserModelInterface, error) { Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &UserMgo{coll: coll}, nil } diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index b8184d767..e07c5b7f4 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -27,8 +27,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw/specialerror" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) @@ -63,9 +61,9 @@ func NewMongo() (*Mongo, error) { time.Sleep(time.Second) // exponential backoff could be implemented here continue } - return nil, err + return nil, errs.Wrap(err) } - return nil, err + return nil, errs.Wrap(err) } func buildMongoURI() string { @@ -150,7 +148,7 @@ func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...strin _, err := indexView.CreateOne(context.Background(), index, opts) if err != nil { - return utils.Wrap(err, "CreateIndex") + return errs.Wrap(err, "CreateIndex") } return nil } diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 1eb7b522a..05006f582 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -16,6 +16,8 @@ package kafka import ( "context" + "github.com/OpenIMSDK/tools/errs" + "strings" "github.com/OpenIMSDK/tools/log" @@ -36,7 +38,7 @@ type MConsumerGroupConfig struct { IsReturnErr bool } -func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string) *MConsumerGroup { +func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string) (*MConsumerGroup, error) { consumerGroupConfig := sarama.NewConfig() consumerGroupConfig.Version = consumerConfig.KafkaVersion consumerGroupConfig.Consumer.Offsets.Initial = consumerConfig.OffsetsInitial @@ -49,26 +51,28 @@ func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []str SetupTLSConfig(consumerGroupConfig) consumerGroup, err := sarama.NewConsumerGroup(addrs, groupID, consumerGroupConfig) if err != nil { - panic(err.Error()) + return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID) } return &MConsumerGroup{ consumerGroup, groupID, topics, - } + }, nil } func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) context.Context { return GetContextWithMQHeader(cMsg.Headers) } -func (mc *MConsumerGroup) RegisterHandleAndConsumer(handler sarama.ConsumerGroupHandler) { +func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) { log.ZDebug(context.Background(), "register consumer group", "groupID", mc.groupID) - ctx := context.Background() for { err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler) if err != nil { - panic(err.Error()) + log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID) + } + if ctx.Err() != nil { + return } } } diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 06b1e2b4c..b9f0b4656 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "errors" + "github.com/OpenIMSDK/tools/errs" "strings" "time" @@ -44,7 +45,7 @@ type Producer struct { } // NewKafkaProducer initializes a new Kafka producer. -func NewKafkaProducer(addr []string, topic string) *Producer { +func NewKafkaProducer(addr []string, topic string) (*Producer, error) { p := Producer{ addr: addr, topic: topic, @@ -87,17 +88,17 @@ func NewKafkaProducer(addr []string, topic string) *Producer { for i := 0; i <= maxRetry; i++ { p.producer, err = sarama.NewSyncProducer(p.addr, p.config) if err == nil { - return &p + return &p, nil } time.Sleep(1 * time.Second) // Wait before retrying } // Panic if unable to create producer after retries if err != nil { - panic("Failed to create Kafka producer: " + err.Error()) + return nil, errs.Wrap(errors.New("failed to create Kafka producer: " + err.Error())) } - return &p + return &p, nil } // configureProducerAck configures the producer's acknowledgement level. diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 8295404d3..31fe4fdd5 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -17,6 +17,7 @@ package startrpc import ( "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "log" "net" "net/http" @@ -43,7 +44,6 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/network" - "github.com/OpenIMSDK/tools/utils" ) // Start rpc server. @@ -61,20 +61,20 @@ func Start( net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)), ) if err != nil { - return err + return errs.Wrap(err, network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)) } defer listener.Close() client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) if err != nil { - return utils.Wrap1(err) + return errs.Wrap(err) } defer client.Close() client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) registerIP, err := network.GetRpcRegisterIP(config.Config.Rpc.RegisterIP) if err != nil { - return err + return errs.Wrap(err) } var reg *prometheus.Registry @@ -96,7 +96,7 @@ func Start( err = rpcFn(client, srv) if err != nil { - return utils.Wrap1(err) + return errs.Wrap(err) } err = client.Register( rpcRegisterName, @@ -105,7 +105,7 @@ func Start( grpc.WithTransportCredentials(insecure.NewCredentials()), ) if err != nil { - return utils.Wrap1(err) + return errs.Wrap(err) } var wg errgroup.Group @@ -123,7 +123,7 @@ func Start( }) wg.Go(func() error { - return utils.Wrap1(srv.Serve(listener)) + return errs.Wrap(srv.Serve(listener)) }) sigs := make(chan os.Signal, 1) @@ -146,7 +146,7 @@ func Start( return gerr case <-time.After(15 * time.Second): - return utils.Wrap1(errors.New("timeout exit")) + return errs.Wrap(errors.New("timeout exit")) } } From bb862bd20768d0b2701cca1ef8d1e3fd3322169e Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Fri, 2 Feb 2024 11:41:01 +0800 Subject: [PATCH 06/74] cicd: bump League Patch (#1863) --- pkg/rpcclient/third.go | 3 ++- tools/component/component.go | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index 0d5708fc8..73d874005 100755 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -16,10 +16,11 @@ package rpcclient import ( "context" + "net/url" + "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "google.golang.org/grpc" - "net/url" "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" diff --git a/tools/component/component.go b/tools/component/component.go index 4200b46f5..823d8174b 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -24,6 +24,7 @@ import ( "github.com/OpenIMSDK/tools/component" "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "gopkg.in/yaml.v3" @@ -305,4 +306,4 @@ func getMinioAddr(key1, key2, key3, fallback string) (string, error) { endpoint = strings.Join(arr, ":") } return endpoint, nil -} \ No newline at end of file +} From f551b50e7909f012457add662c42937be10dd6d7 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:21:14 +0800 Subject: [PATCH 07/74] fix: Refactoring the code for component detection (#1868) * feat: add component check func * fix: fix the outpu error * fix: fix the stderr outpu * fix: fix the component check func * fix: fix the error * fix: fix the output error * fix: del the disruptions code * fix the log output format * fix: fix the tools version --- go.mod | 2 +- go.sum | 4 +- pkg/common/db/cache/init_redis.go | 5 +- pkg/common/db/unrelation/mongo.go | 4 +- .../discoveryregister/zookeeper/zookeeper.go | 14 +- pkg/common/kafka/consumer_group.go | 2 +- tools/component/component.go | 249 +++++++----------- 7 files changed, 115 insertions(+), 165 deletions(-) diff --git a/go.mod b/go.mod index a5fecb5ee..16a10a945 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.48 - github.com/OpenIMSDK/tools v0.0.31 + github.com/OpenIMSDK/tools v0.0.32 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index fbd9366c3..136035cef 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.31 h1:fSrhcPTvHEMTSyrJZDupe730mL4nuhvSOUP/BaZiHaY= -github.com/OpenIMSDK/tools v0.0.31/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= +github.com/OpenIMSDK/tools v0.0.32 h1:b8KwtxXKZTsyyHUcZ4OtSo6s/vVXx4HjMuPxH7Kb7Gg= +github.com/OpenIMSDK/tools v0.0.32/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index c593089e6..3cec73be5 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -77,9 +77,10 @@ func NewRedis() (redis.UniversalClient, error) { defer cancel() err = rdb.Ping(ctx).Err() if err != nil { - return nil, errs.Wrap(fmt.Errorf("redis ping %w", err)) + uriFormat := "address:%s, username:%s, password:%s, clusterMode:%t, enablePipeline:%t" + errMsg := fmt.Sprintf(uriFormat, config.Config.Redis.Address, config.Config.Redis.Username, config.Config.Redis.Password, config.Config.Redis.ClusterMode, config.Config.Redis.EnablePipeline) + return nil, errs.Wrap(err, errMsg) } - redisClient = rdb return rdb, err } diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index e07c5b7f4..fe89c6b8a 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -61,9 +61,9 @@ func NewMongo() (*Mongo, error) { time.Sleep(time.Second) // exponential backoff could be implemented here continue } - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, uri) } - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, uri) } func buildMongoURI() string { diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index db1a5c6c4..0082e9833 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -15,6 +15,8 @@ package zookeeper import ( + "fmt" + "github.com/OpenIMSDK/tools/errs" "os" "strings" "time" @@ -33,7 +35,7 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er username := getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username) password := getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password) - return openkeeper.NewClient( + zk, err := openkeeper.NewClient( zkAddr, schema, openkeeper.WithFreq(time.Hour), @@ -42,6 +44,16 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er openkeeper.WithTimeout(10), openkeeper.WithLogger(log.NewZkLogger()), ) + if err != nil { + uriFormat := "address:%s, username :%s, password :%s, schema:%s." + errInfo := fmt.Sprintf(uriFormat, + config.Config.Zookeeper.ZkAddr, + config.Config.Zookeeper.Username, + config.Config.Zookeeper.Password, + config.Config.Zookeeper.Schema) + return nil, errs.Wrap(err, errInfo) + } + return zk, nil } // getEnv returns the value of an environment variable if it exists, otherwise it returns the fallback value. diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 05006f582..87e6d5686 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -51,7 +51,7 @@ func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []str SetupTLSConfig(consumerGroupConfig) consumerGroup, err := sarama.NewConsumerGroup(addrs, groupID, consumerGroupConfig) if err != nil { - return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID) + return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, config.Config.Kafka.Username, config.Config.Kafka.Password) } return &MConsumerGroup{ consumerGroup, diff --git a/tools/component/component.go b/tools/component/component.go index 823d8174b..787ca8af6 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -15,9 +15,13 @@ package main import ( - "errors" "flag" "fmt" + "github.com/IBM/sarama" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" + "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "os" "strings" "time" @@ -32,16 +36,12 @@ import ( const ( // defaultCfgPath is the default path of the configuration file. - defaultCfgPath = "../../../../../config/config.yaml" - maxRetry = 300 - componentStartErrCode = 6000 - configErrCode = 6001 + defaultCfgPath = "../../../../../config/config.yaml" + maxRetry = 300 ) var ( - cfgPath = flag.String("c", defaultCfgPath, "Path to the configuration file") - ErrComponentStart = errs.NewCodeError(componentStartErrCode, "ComponentStartErr") - ErrConfig = errs.NewCodeError(configErrCode, "Config file is incorrect") + cfgPath = flag.String("c", defaultCfgPath, "Path to the configuration file") ) func initCfg() error { @@ -55,7 +55,7 @@ func initCfg() error { type checkFunc struct { name string - function func() (string, error) + function func() error } func main() { @@ -67,11 +67,13 @@ func main() { return } + configGetEnv() + checks := []checkFunc{ //{name: "Mysql", function: checkMysql}, {name: "Mongo", function: checkMongo}, - {name: "Minio", function: checkMinio}, {name: "Redis", function: checkRedis}, + {name: "Minio", function: checkMinio}, {name: "Zookeeper", function: checkZookeeper}, {name: "Kafka", function: checkKafka}, } @@ -82,165 +84,81 @@ func main() { } fmt.Printf("Checking components Round %v...\n", i+1) + var err error allSuccess := true for _, check := range checks { - str, err := check.function() + err = check.function() if err != nil { - component.ErrorPrint(fmt.Sprintf("Starting %s failed, %v", check.name, err)) + component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err)) allSuccess = false - break } else { - component.SuccessPrint(fmt.Sprintf("%s connected successfully, %s", check.name, str)) + component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name)) } } if allSuccess { component.SuccessPrint("All components started successfully!") - return } } - os.Exit(1) -} - -// Helper function to get environment variable or default value -func getEnv(key, fallback string) string { - if value, exists := os.LookupEnv(key); exists { - return value - } - return fallback } // checkMongo checks the MongoDB connection without retries -func checkMongo() (string, error) { - mongo := &component.Mongo{ - Address: config.Config.Mongo.Address, - Database: config.Config.Mongo.Database, - Username: config.Config.Mongo.Username, - Password: config.Config.Mongo.Password, - MaxPoolSize: config.Config.Mongo.MaxPoolSize, - } - uri, uriExist := os.LookupEnv("MONGO_URI") - if uriExist { - mongo.URL = uri - } +func checkMongo() error { + _, err := unrelation.NewMongo() + return err +} - str, err := component.CheckMongo(mongo) - if err != nil { - return "", err - } - return str, nil +// checkRedis checks the Redis connection +func checkRedis() error { + _, err := cache.NewRedis() + return err } // checkMinio checks the MinIO connection -func checkMinio() (string, error) { +func checkMinio() error { + // Check if MinIO is enabled if config.Config.Object.Enable != "minio" { - return "", nil + return nil } - - endpoint, err := getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Config.Object.Minio.Endpoint) - if err != nil { - return "", err - } - minio := &component.Minio{ ApiURL: config.Config.Object.ApiURL, - Endpoint: endpoint, - AccessKeyID: getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID), - SecretAccessKey: getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey), + Endpoint: config.Config.Object.Minio.Endpoint, + AccessKeyID: config.Config.Object.Minio.AccessKeyID, + SecretAccessKey: config.Config.Object.Minio.SecretAccessKey, SignEndpoint: config.Config.Object.Minio.SignEndpoint, UseSSL: getEnv("MINIO_USE_SSL", "false"), } - - str, err := component.CheckMinio(minio) - if err != nil { - return "", err - } - return str, nil -} - -// checkRedis checks the Redis connection -func checkRedis() (string, error) { - // Prioritize environment variables - address := getEnv("REDIS_ADDRESS", strings.Join(config.Config.Redis.Address, ",")) - username := getEnv("REDIS_USERNAME", config.Config.Redis.Username) - password := getEnv("REDIS_PASSWORD", config.Config.Redis.Password) - - redis := &component.Redis{ - Address: strings.Split(address, ","), - Username: username, - Password: password, - } - - addresses, err := getAddress("REDIS_ADDRESS", "REDIS_PORT", config.Config.Redis.Address) - if err != nil { - return "", err - } - redis.Address = addresses - - str, err := component.CheckRedis(redis) - if err != nil { - return "", err - } - return str, nil + _, err := component.CheckMinio(minio) + return err } // checkZookeeper checks the Zookeeper connection -func checkZookeeper() (string, error) { - // Prioritize environment variables - - address := getEnv("ZOOKEEPER_ADDRESS", strings.Join(config.Config.Zookeeper.ZkAddr, ",")) - - zk := &component.Zookeeper{ - Schema: getEnv("ZOOKEEPER_SCHEMA", "digest"), - ZkAddr: strings.Split(address, ","), - Username: getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username), - Password: getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password), - } - - addresses, err := getAddress("ZOOKEEPER_ADDRESS", "ZOOKEEPER_PORT", config.Config.Zookeeper.ZkAddr) - if err != nil { - return "", nil - } - zk.ZkAddr = addresses - - str, err := component.CheckZookeeper(zk) - if err != nil { - return "", err - } - return str, nil +func checkZookeeper() error { + _, err := zookeeper.NewZookeeperDiscoveryRegister() + return err } // checkKafka checks the Kafka connection -func checkKafka() (string, error) { +func checkKafka() error { // Prioritize environment variables - username := getEnv("KAFKA_USERNAME", config.Config.Kafka.Username) - password := getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password) - address := getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ",")) - - kafka := &component.Kafka{ - Username: username, - Password: password, - Addr: strings.Split(address, ","), + kafkaStu := &component.Kafka{ + Username: config.Config.Kafka.Username, + Password: config.Config.Kafka.Password, + Addr: config.Config.Kafka.Addr, } - addresses, err := getAddress("KAFKA_ADDRESS", "KAFKA_PORT", config.Config.Kafka.Addr) + _, kafkaClient, err := component.CheckKafka(kafkaStu) if err != nil { - return "", nil - } - kafka.Addr = addresses - - str, kafkaClient, err := component.CheckKafka(kafka) - if err != nil { - return "", err + return err } defer kafkaClient.Close() // Verify if necessary topics exist topics, err := kafkaClient.Topics() if err != nil { - return "", errs.Wrap(err) + return errs.Wrap(err) } requiredTopics := []string{ @@ -251,11 +169,38 @@ func checkKafka() (string, error) { for _, requiredTopic := range requiredTopics { if !isTopicPresent(requiredTopic, topics) { - return "", ErrComponentStart.Wrap(fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic)) + return errs.Wrap(err, fmt.Sprintf("Kafka doesn't contain topic: %v", requiredTopic)) } } - return str, nil + _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ + KafkaVersion: sarama.V2_0_0_0, + OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, + }, []string{config.Config.Kafka.LatestMsgToRedis.Topic}, + config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis) + if err != nil { + return err + } + + _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ + KafkaVersion: sarama.V2_0_0_0, + OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, + }, []string{config.Config.Kafka.MsgToPush.Topic}, + config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo) + if err != nil { + return err + } + + kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ + KafkaVersion: sarama.V2_0_0_0, + OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, + }, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr, + config.Config.Kafka.ConsumerGroupID.MsgToPush) + if err != nil { + return err + } + + return nil } // isTopicPresent checks if a topic is present in the list of topics @@ -268,42 +213,34 @@ func isTopicPresent(topic string, topics []string) bool { return false } -func getAddress(key1, key2 string, fallback []string) ([]string, error) { - address, addrExist := os.LookupEnv(key1) - port, portExist := os.LookupEnv(key2) - - if addrExist && portExist { - addresses := strings.Split(address, ",") - for i, addr := range addresses { - addresses[i] = addr + ":" + port - } - return addresses, nil - } else if !addrExist && portExist { - result := make([]string, len(config.Config.Redis.Address)) - for i, addr := range config.Config.Redis.Address { - add := strings.Split(addr, ":") - result[i] = add[0] + ":" + port - } - return result, nil - } else if addrExist && !portExist { - return nil, errs.Wrap(errors.New("the ZOOKEEPER_PORT of minio is empty")) - } - return fallback, nil +func configGetEnv() { + config.Config.Object.Minio.AccessKeyID = getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID) + config.Config.Object.Minio.SecretAccessKey = getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey) + config.Config.Mongo.Uri = getEnv("MONGO_URI", config.Config.Mongo.Uri) + config.Config.Mongo.Username = getEnv("MONGO_OPENIM_USERNAME", config.Config.Mongo.Username) + config.Config.Mongo.Password = getEnv("MONGO_OPENIM_PASSWORD", config.Config.Mongo.Password) + config.Config.Kafka.Username = getEnv("KAFKA_USERNAME", config.Config.Kafka.Username) + config.Config.Kafka.Password = getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password) + config.Config.Kafka.Addr = strings.Split(getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ",")), ",") + config.Config.Object.Minio.Endpoint = getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Config.Object.Minio.Endpoint) } -func getMinioAddr(key1, key2, key3, fallback string) (string, error) { +func getMinioAddr(key1, key2, key3, fallback string) string { // Prioritize environment variables endpoint := getEnv(key1, fallback) address, addressExist := os.LookupEnv(key2) port, portExist := os.LookupEnv(key3) if portExist && addressExist { endpoint = "http://" + address + ":" + port - } else if !portExist && addressExist { - return "", errs.Wrap(errors.New("the MINIO_PORT of minio is empty")) - } else if portExist && !addressExist { - arr := strings.Split(config.Config.Object.Minio.Endpoint, ":") - arr[2] = port - endpoint = strings.Join(arr, ":") + return endpoint } - return endpoint, nil + return endpoint +} + +// Helper function to get environment variable or default value +func getEnv(key, fallback string) string { + if value, exists := os.LookupEnv(key); exists { + return value + } + return fallback } From af878a96cfca897906a57363c0754efe117dbeea Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Fri, 2 Feb 2024 19:47:16 +0800 Subject: [PATCH 08/74] feat: add architecture layers (#1860) * feat: add architecture layers * feat: add architecture layers * feat: add architecture layers * feat: add .gitignore file * feat: add .gitignore file * feat: fix openim logs and ci * feat: fix openim logs and ci * feat: support openim readme docs Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * Update main.go --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- CONTRIBUTING-zh_CN.md | 33 ++++ CONTRIBUTING.md | 211 +++++++++++++++++----- README-zh_CN.md | 58 ++++-- README.md | 39 +++- docs/README.md | 1 - docs/README_ar.md | 67 ------- docs/README_cs.md | 67 ------- docs/README_da.md | 67 ------- docs/README_de.md | 66 ------- docs/README_el.md | 67 ------- docs/README_eo.md | 67 ------- docs/README_es.md | 67 ------- docs/README_fa.md | 67 ------- docs/README_fi.md | 67 ------- docs/README_fr.md | 66 ------- docs/README_hu.md | 67 ------- docs/README_id.md | 67 ------- docs/README_it.md | 67 ------- docs/README_ja.md | 67 ------- docs/README_ko.md | 67 ------- docs/README_ml.md | 67 ------- docs/README_nl.md | 67 ------- docs/README_pl.md | 67 ------- docs/README_pt_BR.md | 67 ------- docs/README_ru.md | 67 ------- docs/README_tr.md | 67 ------- docs/README_ua.md | 67 ------- docs/README_vi.md | 67 ------- docs/README_zh_CN.md | 67 ------- docs/README_zh_TW.md | 67 ------- docs/contributing/CONTRIBUTING-JP.md | 33 ++++ docs/contributing/CONTRIBUTING-PL.md | 33 ++++ docs/images/Open-IM-Servers-on-System.png | Bin 21614 -> 0 bytes docs/images/Open-IM-Servers-on-docker.png | Bin 10172 -> 0 bytes docs/images/architecture-layers.png | Bin 0 -> 293222 bytes docs/images/build.png | Bin 62555 -> 0 bytes docs/images/docker_build.png | Bin 58699 -> 0 bytes docs/readme/README-UA.md | 7 + 38 files changed, 346 insertions(+), 1742 deletions(-) create mode 100644 CONTRIBUTING-zh_CN.md delete mode 100644 docs/README_ar.md delete mode 100644 docs/README_cs.md delete mode 100644 docs/README_da.md delete mode 100644 docs/README_de.md delete mode 100644 docs/README_el.md delete mode 100644 docs/README_eo.md delete mode 100644 docs/README_es.md delete mode 100644 docs/README_fa.md delete mode 100644 docs/README_fi.md delete mode 100644 docs/README_fr.md delete mode 100644 docs/README_hu.md delete mode 100644 docs/README_id.md delete mode 100644 docs/README_it.md delete mode 100644 docs/README_ja.md delete mode 100644 docs/README_ko.md delete mode 100644 docs/README_ml.md delete mode 100644 docs/README_nl.md delete mode 100644 docs/README_pl.md delete mode 100644 docs/README_pt_BR.md delete mode 100644 docs/README_ru.md delete mode 100644 docs/README_tr.md delete mode 100644 docs/README_ua.md delete mode 100644 docs/README_vi.md delete mode 100644 docs/README_zh_CN.md delete mode 100644 docs/README_zh_TW.md create mode 100644 docs/contributing/CONTRIBUTING-JP.md create mode 100644 docs/contributing/CONTRIBUTING-PL.md delete mode 100644 docs/images/Open-IM-Servers-on-System.png delete mode 100644 docs/images/Open-IM-Servers-on-docker.png create mode 100644 docs/images/architecture-layers.png delete mode 100644 docs/images/build.png delete mode 100644 docs/images/docker_build.png create mode 100644 docs/readme/README-UA.md diff --git a/CONTRIBUTING-zh_CN.md b/CONTRIBUTING-zh_CN.md new file mode 100644 index 000000000..ee3c0b8f8 --- /dev/null +++ b/CONTRIBUTING-zh_CN.md @@ -0,0 +1,33 @@ +# How do I contribute code to OpenIM + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + + +

diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee275e7ad..f7d2c0749 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,38 @@ -# Contributing to Open-IM-Server - -So, you want to hack on Open-IM-Server? Yay! +# How do I contribute code to OpenIM + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + + +

+ +So, you want to hack on open-im-server? Yay! First of all, thank you for considering contributing to our project! We appreciate your time and effort, and we value any contribution, whether it's reporting a bug, suggesting a new feature, or submitting a pull request. @@ -12,7 +44,7 @@ This document provides guidelines and best practices to help you contribute effe ## 📇Topics -- [Contributing to Open-IM-Server](#contributing-to-open-im-server) +- [How do I contribute code to OpenIM](#how-do-i-contribute-code-to-openim) - [📇Topics](#topics) - [What we expect of you](#what-we-expect-of-you) - [Code of ConductCode of Conduct](#code-of-conductcode-of-conduct) @@ -32,13 +64,13 @@ This document provides guidelines and best practices to help you contribute effe ## What we expect of you -We hope that anyone can join Open-IM-Server , even if you are a student, writer, translator +We hope that anyone can join open-im-server , even if you are a student, writer, translator -Please meet the minimum version of the Go language published in [go.mod](./go.mod). If you want to manage the Go language version, we provide tools to install [gvm](https://github.com/moovweb/gvm) in our [Makefile](./Makefile) +Please meet the minimum version of the Go language published in [go.mod](./go.mod). If you want to manage the Go language version, we provide tools tHow do I contribute code to OpenIMo install [gvm](https://github.com/moovweb/gvm) in our [Makefile](./Makefile) -You'd better use Linux OR WSL as the development environment, Linux with [Makefile](./Makefile) can help you quickly build and test Open-IM-Server project. +You'd better use Linux OR WSL as the development environment, Linux with [Makefile](./Makefile) can help you quickly build and test open-im-server project. -If you are familiar with [Makefile](./Makefile) , you can easily see the clever design of the Open-IM-Server Makefile. Storing the necessary tools such as golangci in the `/tools` directory can avoid some tool version issues. +If you are familiar with [Makefile](./Makefile) , you can easily see the clever design of the open-im-server Makefile. Storing the necessary tools such as golangci in the `/tools` directory can avoid some tool version issues. The [Makefile](./Makefile) is for every developer, even if you don't know how to use the Makefile tool, don't worry, we provide two great commands to get you up to speed with the Makefile architecture, `make help` and `make help-all`, it can reduce problems of the developing environment. @@ -52,7 +84,7 @@ In accordance with the naming conventions adopted by OpenIM and drawing referenc #### Code and doc contribution -Every action to make project Open-IM-Server better is encouraged. On GitHub, every improvement for Open-IM-Server could be via a [PR](https://github.com/openimsdk/open-im-server/pulls) (short for pull request). +Every action to make project open-im-server better is encouraged. On GitHub, every improvement for open-im-server could be via a [PR](https://github.com/openimsdk/open-im-server/pulls) (short for pull request). + If you find a typo, try to fix it! + If you find a bug, try to fix it! @@ -67,8 +99,8 @@ Every action to make project Open-IM-Server better is encouraged. On GitHub, eve #### Where should I start? -+ If you are new to the project, don't know how to contribute Open-IM-Server, please check out the [good first issue](https://github.com/openimsdk/open-im-server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label. -+ You should be good at filtering the Open-IM-Server issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Abug+) fixes. ++ If you are new to the project, don't know how to contribute open-im-server, please check out the [good first issue](https://github.com/openimsdk/open-im-server/issues?q=is%3Aopen+label%3A"good+first+issue"+sort%3Aupdated-desc) label. ++ You should be good at filtering the open-im-server issue tags and finding the ones you like, such as [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) for big initiatives, features for [feature](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Afeature) proposals, and [bug](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+label%3Abug+) fixes. + If you are looking for something to work on, check out our [open issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc). + If you have an idea for a new feature, please [open an issue](https://github.com/openimsdk/open-im-server/issues/new/choose), and we can discuss it. @@ -85,7 +117,7 @@ When documenting a new design, we recommend a 2-step approach: 1. Use the short-form RFC template to outline your ideas and get early feedback. 2. Once you have received sufficient feedback and consensus, you may use the longer-form design doc template to specify and discuss your design in more details. -In order to contribute a feature to Open-IM-Server you'll need to go through the following steps: +In order to contribute a feature to open-im-server you'll need to go through the following steps: + Discuss your idea with the appropriate [working groups](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) on the working group's Slack channel. + Once there is general agreement that the feature is useful, create a GitHub issue to track the discussion. The issue should include information about the requirements and use cases that it is trying to address. @@ -95,13 +127,27 @@ But keep in mind that there is no guarantee of it being accepted and so it is us ## Getting Started -To propose PR for the Open-IM-Server item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps: +To propose PR for the open-im-server item, we assume you have registered a GitHub ID. Then you could finish the preparation in the following steps: + +1. Fork the repository(open-im-server) + +2. **CLONE** your own repository to main locally. Use `git clone https://github.com//open-im-server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make. + +3. **Initialize Git Hooks with `make init-githooks`** + + After cloning the repository, it's recommended to set up Git hooks to streamline your workflow and ensure your contributions adhere to OpenIM's community standards. Git hooks are scripts that run automatically every time a particular event occurs in a Git repository, such as before a commit or push. To initialize Git hooks for the OpenIM server repository, use the `make init-githooks` command. -1. Fork the repository(Open-IM-Server) + - **Enabling Git Hooks Mode**: By running `make init-githooks` and entering `1` when prompted, you enable Git hooks mode. This action will generate a series of hooks within the `.git/hooks/` directory. These hooks impose certain checks on your commits and pushes, ensuring they meet the quality and standards expected by the OpenIM community. For instance, commit hooks might enforce a specific commit message format, while push hooks could check for code style or linting issues. -2. **CLONE** your own repository to main locally. Use `git clone https://github.com//Open-IM-Server.git` to clone repository to your local machine. Then you can create new branches to finish the change you wish to make. + - **Benefits for First-Time Contributors**: This setup is especially beneficial for new contributors. It guides you to make professional, community-standard-compliant Pull Requests (PRs) and PR descriptions right from your first contribution. By automating checks and balances, it reduces the chances of common mistakes and speeds up the review process. -3. **Set Remote** upstream to be `https://github.com/openimsdk/open-im-server.git` using the following two commands: + - **Disabling Git Hooks**: If for any reason you wish to remove the Git hooks, simply run `make init-githooks` again and enter `2` when prompted. This will delete the existing Git hooks, removing the automatic checks and constraints from your Git operations. However, keep in mind that manually ensuring your contributions adhere to community standards without the aid of Git hooks requires diligence. + + > [!NOTE] Utilizing Git hooks through the `make init-githooks` command is a straightforward yet powerful way to ensure your contributions are consistent and high-quality. It's a step towards fostering a professional and efficient development environment in the OpenIM project. + + + +4. **Set Remote** upstream to be `https://github.com/openimsdk/open-im-server.git` using the following two commands: ```bash ❯ git remote add upstream https://github.com/openimsdk/open-im-server.git @@ -112,18 +158,18 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu ```bash ❯ git remote -v - origin https://github.com//Open-IM-Server.git (fetch) - origin https://github.com//Open-IM-Server.git (push) + origin https://github.com//open-im-server.git (fetch) + origin https://github.com//open-im-server.git (push) upstream https://github.com/openimsdk/open-im-server.git (fetch) upstream no-pushing (push) ``` Adding this, we can easily synchronize local branches with upstream branches. -4. Create a new branch for your changes (use a descriptive name, such as `fix-bug-123` or `add-new-feature`). +5. Create a new branch for your changes (use a descriptive name, such as `fix-bug-123` or `add-new-feature`). ```bash - ❯ cd Open-IM-Server + ❯ cd open-im-server ❯ git fetch upstream ❯ git checkout upstream/main ``` @@ -136,7 +182,8 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu Make any change on the `new-branch` then use [Makefile](./Makefile) build and test your codes. -5. **Commit your changes** to your local branch, lint before committing and commit with sign-off + +6. **Commit your changes** to your local branch, lint before committing and commit with sign-off ```bash ❯ git rebase upstream/main @@ -145,7 +192,7 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu ❯ git commit -a -s -m "message for your changes" # -s adds a Signed-off-by trailer ``` -6. **Push your branch** to your forked repository, it is recommended to have only one commit for a PR. +7. **Push your branch** to your forked repository, it is recommended to have only one commit for a PR. ```bash # sync up with upstream @@ -177,12 +224,78 @@ To propose PR for the Open-IM-Server item, we assume you have registered a GitHu ❯ git rebase upstream/main # rebase the current branch to upstream/main branch ❯ git add -A ❯ git commit -m -s "feat: feature two" - # then create pull request, and merge ``` -7. **Open a pull request** to `openimsdk/open-im-server:main` + **Verifying Your Pull Request with `make all` Command** + + Before verifying, you may need to complete the basic deployment of OpenIM to get familiar with the deployment status of OpenIM. Please read [this deployment document](https://docs.openim.io/zh-Hans/guides/gettingStarted/imSourceCodeDeployment), which will tell you how to deploy OpenIM middleware and OpenIM services in detail + + Before submitting your Pull Request (PR), it's crucial to ensure that it passes all the necessary checks and verifications to maintain the quality and integrity of the OpenIM server project. To facilitate this process, we have encapsulated a series of validation steps into the `make all` command. + + - **Purpose of `make all` Command**: The `make all` command serves as a comprehensive pre-PR verification tool. It sequentially executes a variety of tasks designed to scrutinize your changes from multiple angles, ensuring they are ready for submission. + + - **Included Commands**: + - `tidy`: Cleans up the module by removing unused dependencies. + - `gen`: Generates necessary files from templates or specifications, ensuring that your codebase is up-to-date with its dependencies. + - `add-copyright`: Checks for and adds copyright notices to files, ensuring compliance with legal requirements. + - `verify`: Verifies the integrity and consistency of the code, dependencies, and various checks. + - `test-api`: Runs API tests to ensure that your changes do not break any existing functionality and adhere to the expected behaviors. + - `lint`: Analyzes the code for potential stylistic or programming errors, enforcing the project's coding standards. + - `cover`: Measures the code coverage of tests, helping you understand how much of the code is being tested. + - `restart`: (Optionally) restarts services or applications to ensure that changes are correctly applied and functioning in a live environment. + + - **Executing the Command**: To run the `make all` command, simply navigate to the root directory of your cloned repository in your terminal and execute: + ```bash + make all + ``` + This command will sequentially perform all the listed actions, outputting any warnings or errors encountered during the process. It's a vital step to catch any issues early and ensure your contribution meets the quality standards set by the OpenIM community. + + - **Benefits**: By using `make all` for pre-PR verification, you significantly increase the likelihood of your PR being accepted on the first review. It not only demonstrates your commitment to quality but also streamlines the review process by minimizing back-and-forth due to common issues that can be caught automatically. + + + **Troubleshooting Git Push Failures** + + When working with Git, encountering errors during push operations is not uncommon. Two primary reasons you might face push failures are due to firewall restrictions or authentication issues. Here’s how you can troubleshoot and resolve these problems. + + **Firewall Errors** + + If you're behind a corporate firewall or your network restricts certain types of traffic, you might encounter issues when trying to push your changes via HTTPS. This is because firewalls can block the ports used by the HTTPS protocol. To resolve this issue, you can configure Git to use a proxy. + + If you have a local proxy server set up, you can direct Git to use it by setting the `https_proxy` and `http_proxy` environment variables. Open your terminal or command prompt and run the following commands: + + ```bash + export https_proxy="http://127.0.0.1:7890" + export http_proxy="http://127.0.0.1:7890" + ``` + + Replace `127.0.0.1:7890` with the address and port of your proxy server. These commands set the proxy for the current session. If you want to make these changes permanent, add them to your `.bashrc`, `.bash_profile`, or equivalent shell configuration file. + + **Using SSH Instead of HTTPS** + + An alternative to using HTTPS is to set up an SSH connection for Git operations. SSH connections are often not blocked by firewalls and do not require proxy settings. Additionally, SSH provides a secure channel and can simplify the authentication process since it relies on SSH keys rather than username and password credentials. + + To use SSH with Git, you first need to generate an SSH key pair and add the public key to your GitHub account (or another Git hosting service). + + 1. **Generate SSH Key Pair**: Open your terminal and run `ssh-keygen -t rsa -b 4096 -C "your_email@example.com"`, replacing `your_email@example.com` with your email. Press enter to accept the default file location and passphrase prompts. + + 2. **Add SSH Key to SSH-Agent**: Ensure the ssh-agent is running with `eval "$(ssh-agent -s)"` and then add your SSH private key to the ssh-agent using `ssh-add ~/.ssh/id_rsa`. + + 3. **Add SSH Key to GitHub**: Copy your SSH public key to your clipboard with `cat ~/.ssh/id_rsa.pub | clip` (Windows) or `pbcopy < ~/.ssh/id_rsa.pub` (Mac). Go to GitHub, navigate to Settings > SSH and GPG keys, and add a new SSH key, pasting your key into the field provided. + + 4. **Switch to SSH in Your Repository**: Change your repository's remote URL from HTTPS to SSH. You can find the SSH URL in your repository settings on GitHub and use `git remote set-url origin git@github.com:username/repository.git` to switch. + + **Authentication Errors** + + If you're experiencing authentication errors, it might be due to missing or incorrect credentials. Ensure you have added your SSH key to your Git hosting service. You can test your SSH connection with `ssh -T git@github.com` (replace `github.com` with your Git hosting service's domain). If successful, you'll receive a welcome message. + + For HTTPS users, check that your username and password (or personal access token for services like GitHub that no longer accept password authentication for Git operations) are correct. + +8. **Open a pull request** to `openimsdk/open-im-server:main` It is recommended to review your changes before filing a pull request. Check if your code doesn't conflict with the main branch and no redundant code is included. + + > [!TIP] There is a [good blog post documenting](https://nsddd.top/posts/participating-in-this-project/) the entire push contribution process. + ## Style and Specification @@ -190,15 +303,15 @@ We divide the problem into security and general problems: #### Reporting security issues -Security issues are always treated seriously. As our usual principle, we discourage anyone to spread security issues. If you find a security issue of Open-IM-Server, please do not discuss it in public and even do not open a public issue. +Security issues are always treated seriously. As our usual principle, we discourage anyone to spread security issues. If you find a security issue of open-im-server, please do not discuss it in public and even do not open a public issue. Instead we encourage you to send us a private email to info@openim.io to report this. #### Reporting general issues -To be honest, we regard every user of Open-IM-Serveras a very kind contributor. After experiencing Open-IM-Server, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/openimsdk/open-im-server/issues/new/choose). +To be honest, we regard every user of open-im-serveras a very kind contributor. After experiencing open-im-server, you may have some feedback for the project. Then feel free to open an issue via [NEW ISSUE](https://github.com/openimsdk/open-im-server/issues/new/choose). -Since we collaborate project Open-IM-Server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one. +Since we collaborate project open-im-server in a distributed way, we appreciate **WELL-WRITTEN**, **DETAILED**, **EXPLICIT** issue reports. To make the communication more efficient, we wish everyone could search if your issue is an existing one in the searching list. If you find it existing, please add your details in comments under the existing issue instead of opening a brand new one. To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE](https://github.com/OpenIMSDK/.github/tree/main/.github/ISSUE_TEMPLATE) for issue reporters. You can find three kinds of issue templates there: question, bug report and feature request. Please **BE SURE** to follow the instructions to fill fields in template. @@ -206,20 +319,20 @@ To make the issue details as standard as possible, we setup an [ISSUE TEMPLATE]( + bug report + feature request -+ Open-IM-Server performance issues ++ open-im-server performance issues + feature proposal + feature design + help wanted + doc incomplete + test improvement -+ any questions on Open-IM-Server project ++ any questions on open-im-server project + and so on -Also, we must be reminded when submitting a new question about Open-IM-Server, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on. +Also, we must be reminded when submitting a new question about open-im-server, please remember to remove the sensitive data from your post. Sensitive data could be password, secret key, network locations, private business data and so on. #### Commit Rules -Actually in Open-IM-Server, we take two rules serious when committing: +Actually in open-im-server, we take two rules serious when committing: **🥇 Commit Message:** @@ -262,7 +375,7 @@ An example for this could be: #### PR Description -PR is the only way to make change to Open-IM-Server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request. +PR is the only way to make change to open-im-server project files. To help reviewers better get your purpose, PR description could not be too detailed. We encourage contributors to follow the [PR template](https://github.com/OpenIMSDK/.github/tree/main/.github/PULL_REQUEST_TEMPLATE.md) to finish the pull request. You can find some very formal PR in [RFC](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+RFC+label%3ARFC) issues and learn about them. @@ -311,11 +424,11 @@ git() { #### Docs Contribution -The documentation for Open-IM-Server includes: +The documentation for open-im-server includes: -+ [README.md](https://github.com/openimsdk/open-im-server/blob/main/README.md): This file includes the basic information and instructions for getting started with Open-IM-Server. -+ [CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to Open-IM-Server's codebase, such as how to submit issues, pull requests, and code reviews. -+ [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for Open-IM-Server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips. ++ [README.md](https://github.com/openimsdk/open-im-server/blob/main/README.md): This file includes the basic information and instructions for getting started with open-im-server. ++ [CONTRIBUTING.md](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md): This file contains guidelines for contributing to open-im-server's codebase, such as how to submit issues, pull requests, and code reviews. ++ [Official Documentation](https://doc.rentsoft.cn/): This is the official documentation for open-im-server, which includes comprehensive information on all of its features, configuration options, and troubleshooting tips. Please obey the following rules to better format the docs, which would greatly improve the reading experience. @@ -328,20 +441,20 @@ Please obey the following rules to better format the docs, which would greatly i ## Engage to help anything -We choose GitHub as the primary place for Open-IM-Server to collaborate. So the latest updates of Open-IM-Server are always here. Although contributions via PR is an explicit way to help, we still call for any other ways. +We choose GitHub as the primary place for open-im-server to collaborate. So the latest updates of open-im-server are always here. Although contributions via PR is an explicit way to help, we still call for any other ways. + reply to other's [issues](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) if you could; + help solve other user's problems; + help review other's [PR](https://github.com/openimsdk/open-im-server/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc) design; -+ discuss about Open-IM-Server to make things clearer; -+ advocate [Open-IM-Server](https://google.com/search?q=Open-IM-Server) technology beyond GitHub; -+ write blogs on Open-IM-Server and so on. ++ discuss about open-im-server to make things clearer; ++ advocate [open-im-server](https://google.com/search?q=open-im-server) technology beyond GitHub; ++ write blogs on open-im-server and so on. In a word, **ANY HELP IS CONTRIBUTION.** ## Release version -Releases of Open-IM-Server are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this: +Releases of open-im-server are done using [Release Please](https://github.com/googleapis/release-please) and [GoReleaser](https://goreleaser.com/). The workflow looks like this: 🎯 A PR is merged to the `main` branch: @@ -366,17 +479,23 @@ Such a commit can get produced as follows: ❯ git commit --allow-empty -m "chore: release 0.0.3" -m "Release-As: 0.0.3 ```` +For the complex release process, in fact, and encapsulation as CICD, you only need to tag locally, and then publish the tag to OpenIM github to complete the entire OpenIM release process. + +In addition to CICD, we also do a complex release command locally, which can help you complete the full platform compilation, testing, and release to Minio, just by using the `make release` command. +Please [read the detailed documents](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/release.md) + + ## Contact Us -We value close connections with our users, developers, and contributors here at Open-IM-Server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us. +We value close connections with our users, developers, and contributors here at open-im-server. With a large community and maintainer team, we're always here to help and support you. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us. -Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of Open-IM-Server. You can ask technical questions, seek help, or share your experiences with other users of Open-IM-Server. +Our most recommended way to get in touch is through [Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q). Even if you're in China, Slack is usually not blocked by firewalls, making it an easy way to connect with us. Our Slack community is the ideal place to discuss and share ideas and suggestions with other users and developers of open-im-server. You can ask technical questions, seek help, or share your experiences with other users of open-im-server. In addition to Slack, we also offer the following ways to get in touch: -+ : We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 Open-IM-Server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel. ++ : We also have Slack channels for you to communicate and discuss. To join, visit https://slack.com/ and join our [👀 open-im-server slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) team channel. + : Get in touch with us on [Gmail](info@openim.io). If you have any questions or issues that need resolving, or any suggestions and feedback for our open source projects, please feel free to contact us via email. -+ : Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with Open-IM-Server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information. -+ : Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of Open-IM-Server. We will process your request as soon as possible. ++ : Read our [blog](https://doc.rentsoft.cn/). Our blog is a great place to stay up-to-date with open-im-server projects and trends. On the blog, we share our latest developments, tech trends, and other interesting information. ++ : Add [Wechat](https://github.com/OpenIMSDK/OpenIM-Docs/blob/main/docs/images/WechatIMG20.jpeg) and indicate that you are a user or developer of open-im-server. We will process your request as soon as possible. Whether you're looking to join our community or have any questions or suggestions, we welcome you to get in touch with us. diff --git a/README-zh_CN.md b/README-zh_CN.md index 12a56d4f6..e33768881 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -4,29 +4,51 @@

-

- ⭐️ Open source Instant Messaging Server ⭐️
-

- - -

-A+ -good first - - - - -

+
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) -

- English • - 简体中文 • - Docs + Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

+
+

## 🟢 扫描微信进群交流 @@ -39,8 +61,6 @@ OpenIM 是一个专门设计用于在应用程序中集成聊天、音视频通 - - ![App-OpenIM 关系](./docs/images/oepnim-design.png) ## 🚀 关于 OpenIMSDK diff --git a/README.md b/README.md index 3876fcd7a..8721bf2ce 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,35 @@ [![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) [![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) -[**English**](./README.md) • -[**简体中文**](./README-zh_CN.md) • -[**Docs**](https://openim.io/en) + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ @@ -68,6 +94,13 @@ It is built using Golang and supports cross-platform deployment, ensuring a cons 👉 **[Learn more](https://docs.openim.io/guides/introduction/product)** +## :building_construction: Overall Architecture + +Delve into the heart of Open-IM-Server's functionality with our architecture diagram. + +![Overall Architecture](./docs/images/architecture-layers.png) + + ## :rocket: Quick Start We support many platforms. Here are the addresses for quick experience on the web side: diff --git a/docs/README.md b/docs/README.md index 32fc8d015..92ba2c620 100644 --- a/docs/README.md +++ b/docs/README.md @@ -7,7 +7,6 @@ Welcome to the OpenIM Documentation hub! This center provides a comprehensive ra 1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Guidance on contributing and configurations for developers 2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Coding conventions, logging policies, and other transformation tools ------- ## Contrib diff --git a/docs/README_ar.md b/docs/README_ar.md deleted file mode 100644 index bdd616e6a..000000000 --- a/docs/README_ar.md +++ /dev/null @@ -1,67 +0,0 @@ -# وثائق OpenIM Server - -مرحبًا بكم في مركز وثائق OpenIM! يوفر هذا المركز مجموعة شاملة من الأدلة والكتيبات التي صُممت لمساعدتك في الاستفادة القصوى من تجربة OpenIM الخاصة بك. - -## جدول المحتويات - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - إرشادات حول المساهمة والتكوينات للمطورين -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - اتفاقيات الكود، سياسات التسجيل، وأدوات التحويل الأخرى - ------- - -## مساهمة - -هذا القسم يقدم للمطورين دليلاً مفصلاً حول كيفية المساهمة في الكود، إعداد بيئتهم، واتباع العمليات المرتبطة. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - قواعد واتفاقيات لكتابة الكود في OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - دليل حول كيفية القيام بالتطوير داخل OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - إرشادات حول عمليات اختيار الجيت. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - سير عمل الجيت في OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - إرشادات حول إعداد وتهيئة OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - كيفية تثبيت الدوكر على جهازك. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - دليل لإعداد بيئة التطوير على لينكس. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - إرشادات حول كيفية القيام ببعض الأعمال الشائعة محليًا. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - طرق توظيف OpenIM دون اتصال. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - دليل حول استخدام أدوات بروتوك. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - أدوات ومكتبات في OpenIM للغة Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - أفضل الممارسات والأدوات لملفات الصيانة. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - أفضل الممارسات والأدوات للسكربتات. - -## التحويلات - -يقدم هذا القسم مختلف الاتفاقيات والسياسات داخل OpenIM، التي تشمل الكود، السجلات، الإصدارات، والمزيد. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - إرشادات وطرق لتحويلات API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - سياسات واتفاقيات التسجيل في OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - إجراءات واتفاقيات لـ CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - اتفاقيات لالتزامات الكود في OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - هيكل الدليل واتفاقياته داخل OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - قائمة وأوصاف رموز الخطأ. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - اتفاقيات وتحويلات لكود Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - استراتيجيات إدارة صور الدوكر في OpenIM، تشمل عدة معماريات ومستودعات الصور. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - اتفاقيات أكثر تفصيلاً حول التسجيل. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - استراتيجيات التسمية والإدارة لإصدارات OpenIM. - - -## للمطورين والمساهمين ومشرفي المجتمع - -### المطورون والمساهمون - -إذا كنت مطورًا أو شخصًا حريصًا على المساهمة: - -- تعرف على [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) و[Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) لضمان سلاسة المساهمات. -- اغمر نفسك في [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) للتعرف على ممارسات التطوير في OpenIM. - -### مشرفو المجتمع - -كمشرف على المجتمع: - -- تأكد من أن المساهمات تتوافق مع المعايير الموضحة في وثائقنا. -- راجع بانتظام [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) و[Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) للبقاء على اطلاع. - -## للمستخدمين - -يجب أن يولي المستخدمون اهتمامًا خاصًا لـ: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - ضروري إذا كنت تخطط لاستخدام صور الدوكر لـ OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - لفهم الصور المختلفة المتاحة وكيفية اختيار ال diff --git a/docs/README_cs.md b/docs/README_cs.md deleted file mode 100644 index 0c40b63b1..000000000 --- a/docs/README_cs.md +++ /dev/null @@ -1,67 +0,0 @@ -# Dokumenty serveru OpenIM - -Vítejte v centru dokumentace OpenIM! Toto centrum poskytuje komplexní řadu průvodců a manuálů navržených tak, aby vám pomohly co nejlépe využít vaše zkušenosti s OpenIM. - -## Obsah - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Pokyny pro přispívání a konfigurace pro vývojáře -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konvence kódování, zásady protokolování a další transformační nástroje - ------- - -## Contrib - -Tato část nabízí vývojářům podrobný návod, jak přispívat kódem, nastavovat prostředí a sledovat související procesy. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Pravidla a konvence pro psaní kódu v OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Návod, jak provádět vývoj v rámci OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Pokyny pro operace sběru třešní. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Pracovní postup git v OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Pokyny k nastavení a inicializaci OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Jak nainstalovat Docker na váš počítač. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Průvodce nastavením vývojového prostředí na Linuxu. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Pokyny k provádění určitých společných akcí na místní úrovni. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metody nasazení OpenIM offline. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Průvodce používáním protokolových nástrojů. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Nástroje a knihovny v OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Osvědčené postupy a nástroje pro Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Doporučené postupy a nástroje pro skripty. - -## Konverze - -Tato část představuje různé konvence a zásady v rámci OpenIM, zahrnující kód, protokoly, verze a další. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Pokyny a metody pro konverze API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Zásady a konvence protokolování v OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Postupy a konvence pro CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvence pro odevzdání kódu v OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktura adresářů a konvence v rámci OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Seznam a popisy chybových kódů. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvence a převody pro kód Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie správy pro obrazy OpenIM Docker, zahrnující různé architektury a úložiště obrazů. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Další podrobné konvence o protokolování. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie pojmenování a správy verzí OpenIM. - - -## Pro vývojáře, přispěvatele a správce komunity - -### Vývojáři a přispěvatelé - -Pokud jste vývojář nebo někdo, kdo má zájem přispívat: - -- Seznamte se s našimi [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) a [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), abyste zajistili hladké příspěvky. -- Ponořte se do [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), abyste se seznámili s vývojovými postupy v OpenIM. - -### Správci komunity - -Jako správce komunity: - -- Zajistěte, aby příspěvky odpovídaly standardům uvedeným v naší dokumentaci. -- Pravidelně kontrolujte [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) a [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) , abyste zůstali aktuální. - -## Pro uživatele - -Uživatelé by měli věnovat zvláštní pozornost: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nezbytné, pokud plánujete používat obrazy OpenIM Docker. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Chcete-li porozumět různým dostupným obrázkům a jak vybrat ten správný pro vaši architekturu. \ No newline at end of file diff --git a/docs/README_da.md b/docs/README_da.md deleted file mode 100644 index d14c6adb5..000000000 --- a/docs/README_da.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server Dokumentation - -Velkommen til OpenIM Dokumentationscentret! Dette center indeholder en omfattende række vejledninger og manualer, der er designet til at hjælpe dig med at få mest muligt ud af din OpenIM-oplevelse. - -## Indholdsfortegnelse - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Vejledning om bidrag og konfigurationer for udviklere -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodningskonventioner, logningspolitikker og andre transformationsværktøjer - ------- - -## Bidrag - -Denne sektion tilbyder udviklere en detaljeret vejledning om, hvordan de kan bidrage med kode, konfigurere deres miljø og følge de tilknyttede processer. - -- [Kodekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regler og konventioner for at skrive kode i OpenIM. -- [Udviklingsguide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - En guide om, hvordan man udfører udvikling inden for OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Retningslinjer for cherry-picking-operationer. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Git-workflowen i OpenIM. -- [Initialiseringskonfigurationer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Vejledning om opsætning og initialisering af OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Sådan installeres Docker på din maskine. -- [Linux Udviklingsmiljø](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide til opsætning af udviklingsmiljøet på Linux. -- [Lokale handlinger](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Retningslinjer for, hvordan man udfører visse almindelige handlinger lokalt. -- [Offline-deploering](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metoder til at udrulle OpenIM offline. -- [Protoc-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guide til brug af protoc-værktøjer. -- [Go-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Værktøjer og biblioteker i OpenIM til Go. -- [Makefile-værktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Bedste praksis og værktøjer til Makefile. -- [Scriptværktøjer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Bedste praksis og værktøjer til scripts. - -## Konverteringer - -Denne sektion introducerer forskellige konventioner og politikker inden for OpenIM, herunder kode, logfiler, versioner og meget mere. - -- [API-konverteringer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Retningslinjer og metoder til API-konverteringer. -- [Logningspolitik](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politikker og konventioner for logning i OpenIM. -- [CI/CD-handlinger](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedurer og konventioner for CI/CD. -- [Commit-konventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konventioner for kodeforpligtelser i OpenIM. -- [Mappekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Mappens struktur og konventioner inden for OpenIM. -- [Fejlkoder](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste og beskrivelser af fejlkoder. -- [Go-kodekonverteringer](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konventioner og konverteringer for Go-kode. -- [Docker Image-strategi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Styringsstrategier for OpenIM Docker-billeder, der dækker flere arkitekturer og billedarkiver. -- [Logningskonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Yderligere detaljerede konventioner om logning. -- [Versionkonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Navngivnings- og styringsstrategier for OpenIM-versioner. - - -## For udviklere, bidragsydere og samfundsvedligeholdere - -### Udviklere og bidragsydere - -Hvis du er en udvikler eller nogen, der gerne vil bidrage: - -- Gør dig fortrolig med vores [Kodekonventioner](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) og [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) for at sikre en problemfri bidragelse. -- Dyk ned i [Udviklingsguiden](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) for at få en idé om udviklingspraksis i OpenIM. - -### Samfundsvedligeholdere - -Som samfundsvedligeholder: - -- Sørg for, at bidrag stemmer overens med standarderne beskrevet i vores dokumentation. -- Gennemgå regelmæssigt [Logningspolitikken](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) og [Fejlkoderne](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) for at holde dig opdateret. - -## For brugere - -Brugere bør være opmærksomme på følgende: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nødvendigt, hvis du planlægger at bruge Docker-billeder af OpenIM. -- [Docker Image-strategi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - For at forstå de forskellige tilgængelige billeder og hvordan man vælger det rigtige for din arkitektur. diff --git a/docs/README_de.md b/docs/README_de.md deleted file mode 100644 index 101eba41b..000000000 --- a/docs/README_de.md +++ /dev/null @@ -1,66 +0,0 @@ -# OpenIM-Serverdokumente - -Willkommen im OpenIM-Dokumentationshub! Dieses Zentrum bietet eine umfassende Auswahl an Leitfäden und Handbüchern, die Ihnen dabei helfen sollen, das Beste aus Ihrem OpenIM-Erlebnis herauszuholen. - -## Inhaltsverzeichnis - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Anleitung zu Beiträgen und Konfigurationen für Entwickler -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Codierungskonventionen, Protokollierungsrichtlinien und andere Transformationstools - ------- - -## Beitrag - -Dieser Abschnitt bietet Entwicklern eine detaillierte Anleitung zum Beitragen von Code, zum Einrichten ihrer Umgebung und zum Befolgen der zugehörigen Prozesse. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regeln und Konventionen zum Schreiben von Code in OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Eine Anleitung zur Durchführung der Entwicklung innerhalb von OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Richtlinien zur Rosinenpickerei. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Der Git-Workflow in OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Anleitung zum Einrichten und Initialisieren von OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - So installieren Sie Docker auf Ihrem Computer. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Anleitung zum Einrichten der Entwicklungsumgebung unter Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Richtlinien zur Durchführung bestimmter allgemeiner Aktionen vor Ort. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Methoden zur Offline-Bereitstellung von OpenIM. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Anleitung zur Verwendung von Protokolltools. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Tools und Bibliotheken in OpenIM für Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Best Practices und Tools für Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Best Practices und Tools für Skripte. - -## Konvertierungen - -In diesem Abschnitt werden verschiedene Konventionen und Richtlinien innerhalb von OpenIM vorgestellt, die Code, Protokolle, Versionen und mehr umfassen. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Richtlinien und Methoden für API-Konvertierungen. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Protokollierungsrichtlinien und -konventionen in OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Verfahren und Konventionen für CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konventionen für Code-Commits in OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Verzeichnisstruktur und Konventionen innerhalb von OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste und Beschreibungen der Fehlercodes. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konventionen und Konvertierungen für Go-Code. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Verwaltungsstrategien für OpenIM-Docker-Images, die mehrere Architekturen und Image-Repositorys umfassen. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Weitere detaillierte Konventionen zur Protokollierung. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Benennungs- und Verwaltungsstrategien für OpenIM-Versionen. - - -## Für Entwickler, Mitwirkende und Community-Betreuer - -### Entwickler und Mitwirkende - -Wenn Sie Entwickler sind oder gerne einen Beitrag leisten möchten: -- Machen Sie sich mit unseren [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) und unserem [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) vertraut, um reibungslose Beiträge zu gewährleisten. -- Tauchen Sie ein in den [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), um sich mit den Entwicklungspraktiken in OpenIM vertraut zu machen. - -### Community-Betreuer - -Als Community-Betreuer: - -- Stellen Sie sicher, dass die Beiträge den in unserer Dokumentation dargelegten Standards entsprechen. -- Überprüfen Sie regelmäßig die [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) und die [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), um auf dem Laufenden zu bleiben. - -## Für Benutzer - -Benutzer sollten besonders auf Folgendes achten: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Erforderlich, wenn Sie Docker-Images von OpenIM verwenden möchten. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Um die verschiedenen verfügbaren Bilder zu verstehen und wie Sie das richtige für Ihre Architektur auswählen. \ No newline at end of file diff --git a/docs/README_el.md b/docs/README_el.md deleted file mode 100644 index 68438628f..000000000 --- a/docs/README_el.md +++ /dev/null @@ -1,67 +0,0 @@ -# Έγγραφα διακομιστή OpenIM - -Καλώς ήρθατε στο κέντρο τεκμηρίωσης OpenIM! Αυτό το κέντρο παρέχει μια ολοκληρωμένη σειρά οδηγών και εγχειριδίων που έχουν σχεδιαστεί για να σας βοηθήσουν να αξιοποιήσετε στο έπακρο την εμπειρία σας στο OpenIM. - -## Πίνακας περιεχομένων - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Οδηγίες για τη συνεισφορά και τις διαμορφώσεις για προγραμματιστές -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Συμβάσεις κωδικοποίησης, πολιτικές καταγραφής και άλλα εργαλεία μετασχηματισμού - ------- - -## Συνεισφορά - -Αυτή η ενότητα προσφέρει στους προγραμματιστές έναν λεπτομερή οδηγό για το πώς να συνεισφέρουν κώδικα, να ρυθμίσουν το περιβάλλον τους και να ακολουθήσουν τις σχετικές διαδικασίες. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Κανόνες και συμβάσεις για τη σύνταξη κώδικα στο OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Ένας οδηγός για το πώς να πραγματοποιήσετε ανάπτυξη στο OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Οδηγίες για τις εργασίες συλλογής κερασιών. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Η ροή εργασίας git στο OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Οδηγίες για τη ρύθμιση και την προετοιμασία του OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Πώς να εγκαταστήσετε το Docker στο μηχάνημά σας. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Οδηγός για τη ρύθμιση του περιβάλλοντος ανάπτυξης στο Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Οδηγίες για τον τρόπο εκτέλεσης ορισμένων κοινών ενεργειών σε τοπικό επίπεδο. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Μέθοδοι ανάπτυξης OpenIM εκτός σύνδεσης. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Οδηγός χρήσης εργαλείων πρωτοκόλλου. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Εργαλεία και βιβλιοθήκες στο OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Βέλτιστες πρακτικές και εργαλεία για το Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Βέλτιστες πρακτικές και εργαλεία για σενάρια. - -## Μετατροπές - -Αυτή η ενότητα εισάγει διάφορες συμβάσεις και πολιτικές στο OpenIM, που περιλαμβάνουν κώδικα, αρχεία καταγραφής, εκδόσεις και άλλα. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Οδηγίες και μέθοδοι για μετατροπές API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Πολιτικές και συμβάσεις καταγραφής στο OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Διαδικασίες και συμβάσεις για CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Συμβάσεις για δεσμεύσεις κώδικα στο OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Δομή καταλόγου και συμβάσεις στο OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Λίστα και περιγραφές κωδικών σφαλμάτων. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Συμβάσεις και μετατροπές για τον κώδικα Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Στρατηγικές διαχείρισης για εικόνες OpenIM Docker, που εκτείνονται σε πολλαπλές αρχιτεκτονικές και αποθετήρια εικόνων. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Περαιτέρω λεπτομερείς συμβάσεις για την υλοτομία. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Στρατηγικές ονομασίας και διαχείρισης για εκδόσεις OpenIM. - - -## Για προγραμματιστές, συνεισφέροντες και συντηρητές κοινότητας - -### Προγραμματιστές & Συνεισφέροντες - -Εάν είστε προγραμματιστής ή κάποιος που επιθυμεί να συνεισφέρει: - -- Εξοικειωθείτε με τις [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) και [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) για να εξασφαλίσετε ομαλή συνεισφορά. -- Βουτήξτε στον [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) για να δείτε τις πρακτικές ανάπτυξης στο OpenIM. - -### Συντηρητές της Κοινότητας - -Ως συντηρητής κοινότητας: - -- Βεβαιωθείτε ότι οι συνεισφορές ευθυγραμμίζονται με τα πρότυπα που περιγράφονται στην τεκμηρίωσή μας. -- Να ελέγχετε τακτικά την [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) και τους [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) για να ενημερώνεστε. - -## Για Χρήστες - -Οι χρήστες θα πρέπει να δώσουν ιδιαίτερη προσοχή: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Απαραίτητο εάν σκοπεύετε να χρησιμοποιήσετε εικόνες Docker του OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Για να κατανοήσετε τις διάφορες διαθέσιμες εικόνες και πώς να επιλέξετε τη σωστή για την αρχιτεκτονική σας. \ No newline at end of file diff --git a/docs/README_eo.md b/docs/README_eo.md deleted file mode 100644 index d7ae8c08c..000000000 --- a/docs/README_eo.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server Docs - -Bonvenon al la OpenIM Dokumenta nabo! Ĉi tiu centro disponigas ampleksan gamon da gvidiloj kaj manlibroj desegnitaj por helpi vin eltiri la plej grandan parton de via OpenIM-sperto. - -## Enhavtabelo - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Gvidilo pri kontribuado kaj agordoj por programistoj -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodigaj konvencioj, registradaj politikoj kaj aliaj transformaj iloj - ------- - -## Kontribui - -Ĉi tiu sekcio ofertas al programistoj detalan gvidilon pri kiel kontribui kodon, agordi sian medion kaj sekvi la rilatajn procezojn. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Reguloj kaj konvencioj por skribi kodon en OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Gvidilo pri kiel efektivigi disvolviĝon ene de OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Gvidlinioj pri ĉeriz-plukaj operacioj. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - La git-laborfluo en OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Gvidilo pri agordo kaj pravalorigo de OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Kiel instali Docker sur via maŝino. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Gvidilo por agordi la evolumedion en Linukso. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Gvidlinioj pri kiel efektivigi certajn komunajn agojn loke. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metodoj por disfaldi OpenIM eksterrete. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Gvidilo pri uzado de protokaj iloj. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Iloj kaj bibliotekoj en OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Plej bonaj praktikoj kaj iloj por Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Plej bonaj praktikoj kaj iloj por skriptoj. - -## Konvertiĝoj - -Ĉi tiu sekcio enkondukas diversajn konvenciojn kaj politikojn ene de OpenIM, ampleksante kodon, protokolojn, versiojn kaj pli. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Gvidlinioj kaj metodoj por API-konvertoj. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Registrado de politikoj kaj konvencioj en OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Proceduroj kaj konvencioj por CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvencioj por kodoj en OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Dosierujo-strukturo kaj konvencioj ene de OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Listo kaj priskriboj de erarkodoj. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvencioj kaj konvertiĝoj por Go-kodo. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Administradstrategioj por OpenIM Docker-bildoj, ampleksante plurajn arkitekturojn kaj bilddeponejojn. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Pliaj detalaj konvencioj pri arbohakado. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategioj pri nomado kaj administrado por OpenIM-versioj. - - -## Por Programistoj, Kontribuantoj kaj Komunumaj Prizorgantoj - -### Programistoj kaj Kontribuantoj - -Se vi estas programisto aŭ iu fervora kontribui: - -- Familiariĝu kun niaj [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) por certigi glatajn kontribuojn. -- Plonĝu en la[Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) por ekkompreni la evolupraktikojn en OpenIM. - -### Komunumaj Prizorgantoj - -Kiel komunuma prizorganto: - -- Certigu, ke kontribuoj kongruas kun la normoj skizitaj en nia dokumentaro. -- Regule reviziu la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) kaj [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) por resti ĝisdatigita. - -## Por Uzantoj - -Uzantoj devas aparte atenti: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necesas se vi planas uzi Docker-bildojn de OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Por kompreni la malsamajn bildojn disponeblajn kaj kiel elekti la ĝustan por via arkitekturo. \ No newline at end of file diff --git a/docs/README_es.md b/docs/README_es.md deleted file mode 100644 index 96523120a..000000000 --- a/docs/README_es.md +++ /dev/null @@ -1,67 +0,0 @@ -# Documentos del servidor OpenIM - -¡Bienvenido al centro de documentación de OpenIM! Este centro proporciona una amplia gama de guías y manuales diseñados para ayudarle a aprovechar al máximo su experiencia OpenIM. - -## Tabla de contenido - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Orientación sobre contribuciones y configuraciones para desarrolladores -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenciones de codificación, políticas de registro y otras herramientas de transformación - ------- - -## Contribuir - -Esta sección ofrece a los desarrolladores una guía detallada sobre cómo contribuir con código, configurar su entorno y seguir los procesos asociados. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Reglas y convenciones para escribir código en OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Una guía sobre cómo realizar el desarrollo dentro de OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Directrices sobre operaciones de selección selectiva. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - El flujo de trabajo de git en OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Orientación sobre la configuración e inicialización de OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cómo instalar Docker en su máquina. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guía para configurar el entorno de desarrollo en Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Lineamientos sobre cómo llevar a cabo determinadas acciones comunes a nivel local. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Métodos de implementación de OpenIM sin conexión. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guía sobre el uso de herramientas de protocolo. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Herramientas y bibliotecas en OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Mejores prácticas y herramientas para Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Mejores prácticas y herramientas para scripts. - -## Conversiones - -Esta sección presenta varias convenciones y políticas dentro de OpenIM, que abarcan código, registros, versiones y más. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Directrices y métodos para conversiones de API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Políticas y convenciones de registro en OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedimientos y convenciones para CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenciones para confirmaciones de código en OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Estructura de directorios y convenciones dentro de OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista y descripciones de códigos de error. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenciones y conversiones para código Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Estrategias de gestión para imágenes OpenIM Docker, que abarcan múltiples arquitecturas y repositorios de imágenes. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Convenciones más detalladas sobre el registro. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Estrategias de nomenclatura y gestión de versiones OpenIM. - - -## Para desarrolladores, contribuyentes y mantenedores de la comunidad - -### Desarrolladores y colaboradores - -Si eres desarrollador o alguien interesado en contribuir: - -- Familiarícese con nuestras [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) y [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) to ensure smooth contributions. -- Sumérgete en la [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) para familiarizarte con las prácticas de desarrollo en OpenIM. - -### Mantenedores de la comunidad - -Como mantenedor de la comunidad: - -- Asegúrese de que las contribuciones se alineen con los estándares descritos en nuestra documentación. -- Revise periódicamente la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) y los [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) para mantenerse actualizado. - -## Para usuarios - -Los usuarios deben prestar especial atención a: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necesario si planea utilizar imágenes Docker de OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Comprender las diferentes imágenes disponibles y cómo elegir la adecuada para su arquitectura. \ No newline at end of file diff --git a/docs/README_fa.md b/docs/README_fa.md deleted file mode 100644 index 43056647a..000000000 --- a/docs/README_fa.md +++ /dev/null @@ -1,67 +0,0 @@ -# اسناد سرور OpenIM - -به مرکز اسناد OpenIM خوش آمدید! این مرکز طیف گسترده ای از راهنماها و راهنماها را ارائه می دهد که به شما کمک می کند تا از تجربه OpenIM خود بیشترین بهره را ببرید. - -## فهرست مطالب - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - راهنمایی در مورد مشارکت و تنظیمات برای توسعه دهندگان -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - کنوانسیون های کدگذاری، سیاست های ورود به سیستم و سایر ابزارهای تبدیل - ------- - -## مشارکت - -این بخش به توسعه دهندگان راهنمای دقیقی در مورد نحوه مشارکت کد، تنظیم محیط خود و پیروی از فرآیندهای مرتبط ارائه می دهد. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - قوانین و مقررات برای نوشتن کد در OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - راهنمای نحوه انجام توسعه در OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - دستورالعمل عملیات چیدن گیلاس -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - گردش کار git در OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - راهنمایی در مورد راه اندازی و مقداردهی اولیه OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - چگونه داکر را روی دستگاه خود نصب کنیم. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - راهنمای راه اندازی محیط توسعه در لینوکس. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - رهنمودهایی در مورد نحوه انجام برخی از اقدامات مشترک به صورت محلی. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - روش های استقرار OpenIM به صورت آفلاین -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - راهنمای استفاده از ابزار پروتک -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - ابزارها و کتابخانه ها در OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - بهترین روش ها و ابزارها برای Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - بهترین روش ها و ابزارها برای اسکریپت ها. - -## تبدیل ها - -این بخش قراردادها و سیاست‌های مختلفی را در OpenIM معرفی می‌کند که شامل کد، گزارش‌ها، نسخه‌ها و موارد دیگر می‌شود. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - دستورالعمل ها و روش های تبدیل API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - سیاست‌های ورود به سیستم و قراردادها در OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - رویه ها و قراردادها برای CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - کنوانسیون‌ها برای تعهدات کد در OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - ساختار دایرکتوری و قراردادها در OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - لیست و توضیحات کدهای خطا -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - کنوانسیون ها و تبدیل ها برای کد Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - استراتژی های مدیریتی برای تصاویر OpenIM Docker، شامل چندین معماری و مخازن تصویر. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - کنوانسیون های دقیق تر در مورد ورود به سیستم. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - استراتژی های نامگذاری و مدیریت برای نسخه های OpenIM. - - -## برای توسعه‌دهندگان، مشارکت‌کنندگان و نگهبانان انجمن - -### توسعه دهندگان و مشارکت کنندگان - -اگر توسعه‌دهنده هستید یا کسی که مشتاق مشارکت است: - -- - برای اطمینان از مشارکت های روان، با [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) و [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) ما آشنا شوید. -- - در [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) شیرجه بزنید تا از شیوه های توسعه در OpenIM مطلع شوید. - -### نگهبانان جامعه - -به عنوان یک نگهدارنده جامعه: - -- اطمینان حاصل کنید که مشارکت ها با استانداردهای ذکر شده در اسناد ما مطابقت دارند. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) و [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) را به‌طور مرتب مرور کنید تا به‌روز بمانید. - -## برای کاربران - -کاربران باید توجه ویژه ای به موارد زیر داشته باشند: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - اگر قصد دارید از تصاویر Docker OpenIM استفاده کنید، ضروری است. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - برای درک تصاویر مختلف موجود و نحوه انتخاب تصویر مناسب برای معماری خود. \ No newline at end of file diff --git a/docs/README_fi.md b/docs/README_fi.md deleted file mode 100644 index 9678763d9..000000000 --- a/docs/README_fi.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server Docs - -Tervetuloa OpenIM-dokumentaatiokeskukseen! Tämä keskus tarjoaa kattavan valikoiman oppaita ja oppaita, jotka on suunniteltu auttamaan sinua saamaan kaiken irti OpenIM-kokemuksestasi. - -## Sisällysluettelo - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Ohjeita kehittäjille osallistumiseen ja määrityksiin -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Koodauskäytännöt, lokikäytännöt ja muut muunnostyökalut - ------- - -## Contrib - -Tämä osio tarjoaa kehittäjille yksityiskohtaisen oppaan koodin lisäämisestä, ympäristön määrittämisestä ja siihen liittyvien prosessien seuraamisesta. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Säännöt ja käytännöt koodin kirjoittamiselle OpenIM:ssä. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Opas kehitystyön toteuttamiseen OpenIM:ssä. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Kirsikoiden poimimista koskevat ohjeet. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Git-työnkulku OpenIM:ssä. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Ohjeita OpenIM:n käyttöönottoon ja alustamiseen. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Kuinka asentaa Docker koneellesi. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Ohje kehitysympäristön määrittämiseen Linuxissa. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Ohjeita tiettyjen yhteisten toimien toteuttamiseen paikallisesti. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM:n offline-käyttöönottomenetelmät. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Opas protokollatyökalujen käyttöön. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM for Go -työkalut ja kirjastot. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefilen parhaat käytännöt ja työkalut. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Skriptien parhaat käytännöt ja työkalut. - -## Tulokset - -Tässä osiossa esitellään erilaisia OpenIM:n käytäntöjä ja käytäntöjä, jotka kattavat koodin, lokit, versiot ja paljon muuta. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Ohjeita ja menetelmiä API-muunnoksille. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Kirjauskäytännöt ja käytännöt OpenIM:ssä. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD:n menettelyt ja käytännöt. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM:n koodisitoumusten käytännöt. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Hakemistorakenne ja käytännöt OpenIM:ssä. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Luettelo ja kuvaukset virhekoodeista. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go-koodin sopimukset ja muunnokset. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Hallintastrategiat OpenIM Docker -kuville, jotka kattavat useita arkkitehtuureja ja kuvavarastoja. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Tarkemmat hakkuiden käytännöt. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Nimeämis- ja hallintastrategiat OpenIM-versioille. - - -## Kehittäjille, avustajille ja yhteisön ylläpitäjille - -### Kehittäjät ja avustajat - -Jos olet kehittäjä tai joku, joka haluaa osallistua: - -- Tutustu [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md)- ja [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) -käytäntöihimme varmistaaksesi sujuvan osallistumisen. -- Sukella [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) saadaksesi tietoa OpenIM:n kehityskäytännöistä. - -### Yhteisön ylläpitäjät - -Yhteisön ylläpitäjänä: - -- Varmista, että osallistumiset ovat asiakirjoissamme esitettyjen standardien mukaisia. -- Tarkista säännöllisesti [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) ja [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) pysyäksesi ajan tasalla. - -## Käyttäjille - -Käyttäjien tulee kiinnittää erityistä huomiota: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Välttämätön, jos aiot käyttää OpenIM:n Docker-kuvia. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Ymmärtääksesi saatavilla olevat erilaiset kuvat ja kuinka valita oikea arkkitehtuuriisi. \ No newline at end of file diff --git a/docs/README_fr.md b/docs/README_fr.md deleted file mode 100644 index 747ef5349..000000000 --- a/docs/README_fr.md +++ /dev/null @@ -1,66 +0,0 @@ -# Documentation du serveur OpenIM - -Bienvenue dans le hub de documentation OpenIM ! Ce centre propose une gamme complète de guides et de manuels conçus pour vous aider à tirer le meilleur parti de votre expérience OpenIM. - -## Table des matières - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Conseils sur la contribution et les configurations pour les développeurs -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Conventions de codage, politiques de journalisation et autres outils de transformation - ------- - -## Contribuer - -Cette section propose aux développeurs un guide détaillé sur la façon de contribuer au code, de configurer leur environnement et de suivre les processus associés. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Règles et conventions pour écrire du code dans OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Un guide sur la façon de réaliser du développement au sein d'OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Lignes directrices sur les opérations de triage. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Le flux de travail git dans OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Conseils sur la configuration et l’initialisation d’OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Comment installer Docker sur votre machine. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide pour configurer l'environnement de développement sous Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Des lignes directrices pour mener localement certaines actions communes. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Méthodes de déploiement d'OpenIM hors ligne. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guide sur l'utilisation des outils de protocole. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Outils et bibliothèques dans OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Meilleures pratiques et outils pour Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Meilleures pratiques et outils pour les scripts. - -## Conversions - -Cette section présente diverses conventions et politiques au sein d'OpenIM, englobant le code, les journaux, les versions, etc. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Lignes directrices et méthodes pour les conversions API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politiques et conventions de journalisation dans OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Politiques et conventions de journalisation dans OpenIM. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Politiques et conventions de journalisation dans OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Structure de répertoire et conventions au sein d'OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Liste et descriptions des codes d'erreur. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Conventions et conversions pour le code Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Stratégies de gestion des images OpenIM Docker, couvrant plusieurs architectures et référentiels d'images. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Conventions plus détaillées sur la journalisation. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Stratégies de nommage et de gestion pour les versions OpenIM. - - -## Pour les développeurs, les contributeurs et les responsables de la communauté - -### Développeurs et contributeurs - -Si vous êtes un développeur ou quelqu'un désireux de contribuer : -- Familiarisez-vous avec nos [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) et notre [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) pour garantir des contributions fluides. -- Plongez dans le [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) pour vous familiariser avec les pratiques de développement dans OpenIM. - -### Responsables de la communauté - -En tant que responsable de la communauté : - -- EAssurez-vous que les contributions sont conformes aux normes décrites dans notre documentation. -- onsultez régulièrement la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) et les [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) pour rester à jour. - -## Pour les utilisateurs - -Les utilisateurs doivent prêter une attention particulière à : - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Nécessaire si vous prévoyez d'utiliser des images Docker d'OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Pour comprendre les différentes images disponibles et comment choisir celle qui convient à votre architecture. \ No newline at end of file diff --git a/docs/README_hu.md b/docs/README_hu.md deleted file mode 100644 index 62cb54bb2..000000000 --- a/docs/README_hu.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server Docs - -Üdvözöljük az OpenIM dokumentációs központjában! Ez a központ útmutatók és kézikönyvek átfogó választékát kínálja, amelyek célja, hogy a legtöbbet hozza ki az OpenIM-élményből. - -## Tartalomjegyzék - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Útmutató a fejlesztőknek a hozzájáruláshoz és a konfigurációkhoz -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kódolási konvenciók, naplózási szabályzatok és egyéb átalakítási eszközök - ------- - -## Contrib - -Ez a rész részletes útmutatót nyújt a fejlesztőknek a kód hozzáadásával, a környezet beállításával és a kapcsolódó folyamatok követésével kapcsolatban. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Az OpenIM kódírásának szabályai és konvenciói. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Útmutató az OpenIM-en belüli fejlesztés végrehajtásához. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Útmutató a cseresznyeszedési műveletekhez. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - A git munkafolyamat az OpenIM-ben. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Útmutató az OpenIM beállításához és inicializálásához. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - A Docker telepítése a gépére. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guide to set up the development environment on Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Útmutató a fejlesztői környezet beállításához Linuxon. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Az OpenIM offline üzembe helyezésének módszerei. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Útmutató a protokolleszközök használatához. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Eszközök és könyvtárak az OpenIM for Go-ban. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - A Makefile legjobb gyakorlatai és eszközei. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Bevált módszerek és eszközök a szkriptekhez. - -## Conversions - -Ez a rész az OpenIM különféle konvencióit és szabályzatait mutatja be, beleértve a kódot, a naplókat, a verziókat és egyebeket. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Irányelvek és módszerek az API-konverziókhoz. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Naplózási szabályzatok és konvenciók az OpenIM-ben. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD eljárások és konvenciók. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - A kód véglegesítésének konvenciói az OpenIM-ben. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Címtárszerkezet és konvenciók az OpenIM-en belül. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - A hibakódok listája és leírása. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvenciók és átalakítások a Go kódhoz. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Kezelési stratégiák az OpenIM Docker-képekhez, amelyek több architektúrára és képtárra is kiterjednek. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - További részletes egyezmények a fakitermelésről. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Elnevezési és kezelési stratégiák az OpenIM verziókhoz. - - -## Fejlesztőknek, közreműködőknek és közösségi fenntartóknak - -### Fejlesztők és közreműködők - -Ha Ön fejlesztő vagy valaki, aki szeretne hozzájárulni: - -- A zökkenőmentes hozzájárulás érdekében ismerkedjen meg [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) és [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)-val. -- Merüljön el a [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), hogy megismerje az OpenIM fejlesztési gyakorlatát. - -### Közösségfenntartók - -Közösségfenntartóként: - -- Győződjön meg arról, hogy a hozzájárulások megfelelnek a dokumentációnkban felvázolt szabványoknak. -- Rendszeresen tekintse át a [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) és a [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), hogy naprakész maradjon. - -## Felhasználóknak - -A felhasználóknak különös figyelmet kell fordítaniuk a következőkre: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Szükséges, ha az OpenIM Docker-képeit szeretné használni. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Hogy megértse a rendelkezésre álló különböző képeket, és hogyan válassza ki a megfelelőt az építészetéhez. \ No newline at end of file diff --git a/docs/README_id.md b/docs/README_id.md deleted file mode 100644 index 988e25efe..000000000 --- a/docs/README_id.md +++ /dev/null @@ -1,67 +0,0 @@ -# Dokumen Server OpenIM - -Selamat datang di pusat Dokumentasi OpenIM! Pusat ini menyediakan berbagai panduan dan manual yang komprehensif untuk membantu Anda memaksimalkan pengalaman Anda dengan OpenIM. - -## Daftar Isi - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Panduan tentang kontribusi dan konfigurasi untuk pengembang -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konvensi pengkodean, kebijakan logging, dan alat transformasi lainnya - ------- - -## Contrib - -Bagian ini menawarkan panduan rinci bagi pengembang tentang cara berkontribusi kode, menyiapkan lingkungan mereka, dan mengikuti proses yang terkait. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Aturan dan konvensi untuk menulis kode di OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Panduan tentang cara melakukan pengembangan di dalam OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Pedoman tentang operasi cherry-picking. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Alur kerja git di OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Panduan untuk mengatur dan menginisialisasi OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cara memasang Docker di mesin Anda. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Panduan untuk menyiapkan lingkungan pengembangan di Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Pedoman tentang cara melakukan beberapa tindakan umum secara lokal. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metode penyebaran OpenIM secara offline. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Panduan tentang menggunakan alat protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Alat dan perpustakaan di OpenIM untuk Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Praktik terbaik dan alat untuk Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Praktik terbaik dan alat untuk skrip. - -## Conversions - -Bagian ini memperkenalkan berbagai konvensi dan kebijakan dalam OpenIM, meliputi kode, log, versi, dan lainnya. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Pedoman dan metode untuk konversi API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Kebijakan dan konvensi logging di OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Prosedur dan konvensi untuk CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konvensi untuk commit kode di OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktur direktori dan konvensi dalam OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Daftar dan deskripsi kode kesalahan. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konvensi dan konversi untuk kode Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategi manajemen gambar Docker OpenIM, mencakup berbagai arsitektur dan repositori gambar. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Konvensi logging yang lebih detail. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategi penamaan dan manajemen versi OpenIM. - - -## Untuk Pengembang, Kontributor, dan Pemelihara Komunitas - -### Pengembang & Kontributor - -Jika Anda seorang pengembang atau seseorang yang ingin berkontribusi: - -- Kenali [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) dan [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) kami untuk memastikan kontribusi yang lancar. -- Pelajari [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) untuk memahami praktik pengembangan di OpenIM. - -### Pemelihara Komunitas - -Sebagai pemelihara komunitas: - -- Pastikan kontribusi sesuai dengan standar yang diuraikan dalam dokumentasi kami. -- Tinjau secara teratur [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) dan [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) untuk tetap terupdate. - -## Untuk Pengguna - -Pengguna harus memberikan perhatian khusus kepada: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Diperlukan jika Anda berencana menggunakan gambar Docker dari OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Untuk memahami gambar yang tersedia dan bagaimana memilih yang tepat untuk arsitektur Anda. diff --git a/docs/README_it.md b/docs/README_it.md deleted file mode 100644 index 76e41b5e7..000000000 --- a/docs/README_it.md +++ /dev/null @@ -1,67 +0,0 @@ -# Documentazione del Server OpenIM - -Benvenuti al centro documentazione di OpenIM! Questo centro offre una gamma completa di guide e manuali progettati per aiutarvi a ottenere il massimo dalla vostra esperienza con OpenIM. - -## Indice dei Contenuti - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Guida al contributo e alle configurazioni per gli sviluppatori -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenzioni di codifica, politiche di registrazione e altri strumenti di trasformazione - ------- - -## Contrib - -Questa sezione offre agli sviluppatori una guida dettagliata su come contribuire al codice, configurare il loro ambiente e seguire i processi associati. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regole e convenzioni per scrivere codice in OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Una guida su come svolgere lo sviluppo all'interno di OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Linee guida sulle operazioni di cherry-picking. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Il flusso di lavoro git in OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Guida per configurare e inizializzare OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Come installare Docker sul tuo dispositivo. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guida per configurare l'ambiente di sviluppo su Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Linee guida su come eseguire determinate azioni comuni localmente. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metodi per distribuire OpenIM offline. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guida all'uso degli strumenti protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Strumenti e librerie in OpenIM per Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Migliori pratiche e strumenti per Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Migliori pratiche e strumenti per gli script. - -## Conversions - -Questa sezione introduce varie convenzioni e politiche all'interno di OpenIM, che comprendono codice, registrazioni, versioni e altro. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Linee guida e metodi per le conversioni API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Politiche e convenzioni di registrazione in OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedure e convenzioni per CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenzioni per i commit di codice in OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struttura delle directory e convenzioni all'interno di OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Elenco e descrizioni dei codici di errore. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenzioni e conversioni per il codice Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie di gestione delle immagini Docker di OpenIM, che coprono più architetture e repository di immagini. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Ulteriori dettagliate convenzioni sulla registrazione. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie di denominazione e gestione delle versioni di OpenIM. - - -## Per Sviluppatori, Contributori e Manutentori della Comunità - -### Sviluppatori & Contributori - -Se sei uno sviluppatore o qualcuno interessato a contribuire: - -- Familiarizza con le nostre [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) e [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) per garantire contributi fluidi. -- Approfondisci la [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) per ottenere una conoscenza delle pratiche di sviluppo in OpenIM. - -### Manutentori della Comunità - -Come manutentore della comunità: - -- Assicurati che i contributi siano in linea con gli standard delineati nella nostra documentazione. -- Rivedi regolarmente la [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) e [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) per rimanere aggiornato. - -## Per gli Utenti - -Gli utenti dovrebbero prestare particolare attenzione a: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necessario se si prevede di utilizzare le immagini Docker di OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Per comprendere le diverse immagini disponibili e come scegliere quella giusta per la propria architettura. diff --git a/docs/README_ja.md b/docs/README_ja.md deleted file mode 100644 index 3971037fb..000000000 --- a/docs/README_ja.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIMサーバードキュメンテーション - -OpenIMドキュメンテーションハブへようこそ!このセンターでは、OpenIM体験を最大限に活用するための包括的なガイドとマニュアルを提供しています。 - -## 目次 - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 開発者向けの貢献と設定に関するガイダンス -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - コーディング規約、ログポリシー、その他の変換ツール - ------- - -## 投稿 - -このセクションでは、開発者がコードを貢献し、環境を設定し、関連するプロセスに従う方法についての詳細なガイドを提供します。 - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIMでのコード記述のルールと規約。 -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM内での開発を行うためのガイド。 -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - チェリーピッキング操作のガイドライン。 -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIMにおけるGitのワークフロー。 -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIMの設定と初期化に関するガイダンス。 -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - マシンにDockerをインストールする方法。 -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux上での開発環境の設定ガイド。 -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - ローカルで一般的なアクションを実行する方法に関するガイドライン。 -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIMをオフラインでデプロイする方法。 -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - protocツールの使用ガイド。 -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - GoのためのOpenIM内のツールとライブラリ。 -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefileのベストプラクティスとツール。 -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - スクリプトのベストプラクティスとツール。 - -## Conversions - -このセクションでは、OpenIM内のさまざまな規約とポリシーを紹介します。これには、コード、ログ、バージョンなどが含まれます。 - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API変換のためのガイドラインと方法。 -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIMにおけるログポリシーと規約。 -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CDの手順と規約。 -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIMでのコードコミットのための規約。 -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM内のディレクトリ構造と規約。 -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - エラーコードのリストと説明。 -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Goコードのための規約と変換。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 複数のアーキテクチャとイメージリポジトリにまたがるOpenIM Dockerイメージの管理戦略。 -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - ロギングに関するさらに詳細な規約。 -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIMバージョンの命名と管理戦略。 - - -## 開発者、コントリビューター、コミュニティメンテナー向け - -### 開発者およびコントリビューター - -開発者または貢献に熱心な方へ: - -- スムーズな貢献を確実にするために、私たちの[Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md)と[Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)に慣れ親しみましょう。 -- OpenIMの開発実践に慣れるために、[Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md)をご覧ください。 - -### コミュニティメンテナー - -コミュニティメンテナーとして: - -- 貢献が私たちのドキュメンテーションで概説された基準に沿っていることを確認してください。 -- 最新の情報を得るために、定期的に[Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md)と[Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md)をレビューしてください。 - -## ユーザー向け - -ユーザーは特に以下の点に注意してください: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIMのDockerイメージを使用する予定の場合に必要です。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 利用可能なさまざまなイメージを理解し、アーキテクチャに適したものを選択する方法。 diff --git a/docs/README_ko.md b/docs/README_ko.md deleted file mode 100644 index 9cd728c5d..000000000 --- a/docs/README_ko.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM 서버 문서 - -OpenIM 문서 허브에 오신 것을 환영합니다! 이 센터는 OpenIM 경험을 최대한 활용하는 데 도움이 되도록 다양한 가이드와 매뉴얼을 제공합니다. - -## 목차 - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 개발자를 위한 기여 및 구성에 대한 안내 -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 코딩 규칙, 로깅 정책 및 기타 변환 도구 - ------- - -## 기여 - -이 섹션은 개발자들에게 코드를 기여하는 방법, 환경을 설정하는 방법 및 관련 프로세스를 따르는 방법에 대한 자세한 가이드를 제공합니다. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM에서 코드를 작성하기 위한 규칙 및 규약. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM 내에서 개발을 수행하는 방법에 대한 가이드. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 체리피킹 작업에 대한 지침. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM에서의 깃 워크플로우. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM 설정 및 초기화에 대한 안내. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 컴퓨터에 도커를 설치하는 방법. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - 리눅스에서 개발 환경을 설정하는 가이드. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 일부 일반적인 작업을 로컬에서 수행하는 방법에 대한 지침. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 오프라인에서 OpenIM을 배포하는 방법. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 프로토콜 도구 사용에 대한 가이드. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go용 OpenIM 도구 및 라이브러리. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - 메이크파일을 위한 모범 사례 및 도구. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 스크립트를 위한 모범 사례 및 도구. - -## 전환 - -이 섹션에서는 코드, 로그, 버전 등을 포함하는 OpenIM 내의 다양한 규칙과 정책을 소개합니다. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 변환을 위한 지침 및 방법. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM의 로깅 정책 및 관습. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 절차 및 관습. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM에서 코드 커밋을 위한 관습. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 내의 디렉토리 구조 및 관습. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 오류 코드 목록 및 설명. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 코드를 위한 관습 및 변환. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 다양한 아키텍처 및 이미지 저장소를 아우르는 OpenIM Docker 이미지 관리 전략. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 로깅에 대한 추가적인 상세한 관습. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 버전의 명명 및 관리 전략. - - -## 개발자, 기여자 및 커뮤니티 관리자를 위한 정보 - -### 개발자 및 기여자 - -개발자이거나 기여에 관심이 있다면: - -- 원활한 기여를 위해 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 및 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md)에 익숙해지십시오. -- OpenIM에서의 개발 관행을 파악하려면 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md)를 참조하십시오. - -### 커뮤니티 관리자 - -커뮤니티 관리자로서: - -- 기여가 우리 문서에 명시된 표준에 부합하는지 확인하십시오. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 및 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md)를 정기적으로 검토하여 최신 정보를 유지하십시오. - -## 사용자를 위한 정보 - -사용자는 특히 다음 사항에 주의를 기울여야 합니다: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM의 Docker 이미지를 사용할 계획이라면 필요합니다. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 사용 가능한 다양한 이미지를 이해하고 아키텍처에 적합한 이미지를 선택하는 방법. diff --git a/docs/README_ml.md b/docs/README_ml.md deleted file mode 100644 index 7f3aa8c0d..000000000 --- a/docs/README_ml.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM സെർവർ ഡോക്യുമെന്റേഷൻ - -OpenIM ഡോക്യുമെന്റേഷൻ ഹബ്ബിലേക്ക് സ്വാഗതം! ഈ കേന്ദ്രം OpenIM അനുഭവത്തിൽ നിന്ന് പരമാവധി ഉപയോഗം നേടാൻ സഹായിക്കുന്ന വ്യാപകമായ നിർദേശങ്ങളുടെയും മാനുവലുകളുടെയും ശ്രേണി നൽകുന്നു. - -## ഉള്ളടക്കം - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - ഡെവലപ്പർമാർക്കുള്ള സംഭാവനകൾ നൽകുന്നതിനും കോൺഫിഗറേഷനുകൾക്കുള്ള നിർദേശങ്ങൾ -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - കോഡിംഗ് കൺവെൻഷനുകൾ, ലോഗ്ഗിംഗ് നയങ്ങൾ, മറ്റ് പരിവർത്തന ഉപകരണങ്ങൾ - ------- - -## Contrib - -ഈ ഭാഗം ഡെവലപ്പർമാർക്ക് കോഡ് സംഭാവന നൽകുന്നതിന്റെയും അവരുടെ പരിസ്ഥിതി സജ്ജമാക്കുന്നതിന്റെയും ബന്ധപ്പെട്ട പ്രക്രിയകൾ പിന്തുടരുന്നതിന്റെയും വിശദമായ ഗൈഡ് നൽകുന്നു. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM-ൽ കോഡ് എഴുതുന്നതിന്റെ നിയമങ്ങൾ കൺവെൻഷനുകൾ. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM-ൽ വികസനം നടത്തുന്നതിന്റെ ഗൈഡ്. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - ചെറി-പിക്കിംഗ് ഓപ്പറേഷനുകൾക്കുള്ള മാർഗ്ഗനിർദേശങ്ങൾ. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM-ൽ ഗിറ്റിന്റെ വർക്ക്ഫ്ലോ. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM സജ്ജമാക്കുന്നതിനും ആരംഭിക്കുന്നതിനും നിർദേശങ്ങൾ. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - നിങ്ങളുടെ യന്ത്രത്തിൽ ഡോക്കർ ഇൻസ്റ്റാൾ ചെയ്യുന്ന രീതി. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - ലിനക്സിൽ വികസന പരിസ്ഥിതി സജ്ജമാക്കുന്നതിന്റെ ഗൈഡ്. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - ചില പൊതുവായ നടപടികൾ പ്രദേശികമായി നടത്തുന്നതിന്റെ മാർഗ്ഗനിർദേശങ്ങൾ. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM ഓഫ്‌ലൈൻ ഡിപ്ലോയ് ചെയ്യുന്ന രീതികൾ. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - പ്രോട്ടോക് ഉപകരണങ്ങൾ ഉപയോഗിക്കുന്ന ഗൈഡ്. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go വേണ്ടി OpenIM-ൽ ഉള്ള ഉപകരണങ്ങളും ലൈബ്രറികളും. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile ഉപകരണങ്ങളുടെയും മികച്ച പ്രാക്ടീസുകളുടെയും ഗൈഡ്. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - സ്ക്രിപ്റ്റുകൾക്കുള്ള മികച്ച പ്രാക്ടീസുകളും ഉപകരണങ്ങളും. - -## Conversions - -ഈ ഭാഗം OpenIM-ൽ ഉള്ള വിവിധ കൺവെൻഷനുകളെയും നയങ്ങളെയും ആവിഷ്കരിക്കുന്നു, ഇതിൽ കോഡ്, ലോഗുകൾ, പതിപ്പുകൾ എന്നിവ ഉൾപ്പെടുന്നു. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API കൺവെർഷനുകൾക്കുള്ള നിർദേശങ്ങൾ രീതികൾ. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM-ൽ ലോഗ്ഗിംഗ് നയങ്ങൾ കൺവെൻഷനുകൾ. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD പ്രക്രിയകൾ കൺവെൻഷനുകൾ. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM-ൽ കോഡ് കമ്മിറ്റുകൾക്കുള്ള കൺവെൻഷനുകൾ. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM-ൽ ഡയറക്ടറി ഘടന കൺവെൻഷനുകൾ. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - പിശക് കോഡുകളുടെ പട്ടിക വിവരണങ്ങൾ. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go കോഡിനുള്ള കൺവെൻഷനുകൾ പരിവർത്തനങ്ങൾ. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - വിവിധ ആർക്കിടെക്ചറുകൾ ഇമേജ് റെപ്പോസിറ്ററികൾ ഉൾപ്പെടുന്ന OpenIM Docker ഇമേജുകളുടെ മാനേജ്മെന്റ് സ്ട്രാറ്റജീസ്. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - ലോഗ്ഗിംഗിന്റെ കൂടുതൽ വിശദമായ കൺവെൻഷനുകൾ. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM പതിപ്പുകൾക്കുള്ള നാമകരണ മാനേജ്മെന്റ് സ്ട്രാറ്റജീസ്. - - -## ഡെവലപ്പർമാർക്കും, സംഭാവനകൾ നൽകുന്നവർക്കും, കമ്മ്യൂണിറ്റി മെയിന്റെയിനർമാർക്കും - -### ഡെവലപ്പർമാർ & സംഭാവനകൾ നൽകുന്നവർ - -നിങ്ങൾ ഒരു ഡെവലപ്പർ അല്ലെങ്കിൽ സംഭാവനകൾ നൽകാൻ ആഗ്രഹിക്കുന്ന ആളാണെങ്കിൽ: - -- നിരവധി സംഭാവനകൾ ഉറപ്പാക്കാൻ ഞങ്ങളുടെ [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) എന്നിവയുമായി പരിചിതരാകുക. -- OpenIM-ൽ വികസന പ്രാക്ടീസുകൾ ലഭ്യമാക്കാൻ [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) അന്വേഷിക്കുക. - -### കമ്മ്യൂണിറ്റി മെയിന്റെയിനർമാർ - -ഒരു കമ്മ്യൂണിറ്റി മെയിന്റെയിനറായി: - -- സംഭാവനകൾ ഞങ്ങളുടെ ഡോക്യുമെന്റേഷനിൽ വിവരിച്ച മാനദണ്ഡങ്ങൾക്ക് അനുസൃതമാണെന്ന് ഉറപ്പാക്കുക. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) എന്നിവ പുതുക്കി വായിക്കുക. - -## ഉപയോക്താക്കൾക്ക് - -ഉപയോക്താക്കൾ പ്രത്യേകം ശ്രദ്ധിക്കേണ്ട കാര്യങ്ങൾ: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM-ന്റെ ഡോക്കർ ഇമേജുകൾ ഉപയോഗിക്കാൻ പദ്ധതിയിടുന്നെങ്കിൽ ആവശ്യമാണ്. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - ലഭ്യമായ വിവിധ ഇമേജുകൾ മനസ്സിലാക്കുകയും നിങ്ങളുടെ ആർക്കിടെക്ചറിന് അനുയോജ്യമായത് എങ്ങനെ തിരഞ്ഞെടുക്കണം എന്ന് അറിയുക. diff --git a/docs/README_nl.md b/docs/README_nl.md deleted file mode 100644 index 755145e6f..000000000 --- a/docs/README_nl.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server-documenten - -Welkom bij de OpenIM-documentatiehub! Dit centrum biedt een uitgebreide reeks handleidingen en handleidingen die zijn ontworpen om u te helpen het meeste uit uw OpenIM-ervaring te halen. - -## Inhoudsopgave - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Richtlijnen voor bijdragen en configuraties voor ontwikkelaars -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Codeerconventies, logboekbeleid en andere transformatietools - ------- - -## Draag bij - -Deze sectie biedt ontwikkelaars een gedetailleerde handleiding over hoe ze code kunnen bijdragen, hun omgeving kunnen instellen en de bijbehorende processen kunnen volgen. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regels en conventies voor het schrijven van code in OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Een handleiding voor het uitvoeren van ontwikkelingen binnen OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Richtlijnen voor kersenplukoperaties. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - De git-workflow in OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Begeleiding bij het instellen en initialiseren van OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Hoe Docker op uw machine te installeren. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Handleiding voor het opzetten van de ontwikkelomgeving op Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Richtlijnen voor het lokaal uitvoeren van bepaalde gemeenschappelijke acties. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Methoden voor het offline inzetten van OpenIM. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Handleiding voor het gebruik van protocoltools. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Tools en bibliotheken in OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Best practices en tools voor Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Best practices en tools voor scripts. - -## Conversies - -In deze sectie worden verschillende conventies en beleidsregels binnen OpenIM geïntroduceerd, waaronder code, logs, versies en meer. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Richtlijnen en methoden voor API-conversies. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Logboekbeleid en -conventies in OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedures en conventies voor CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Conventies voor code-commits in OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Directorystructuur en conventies binnen OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lijst en beschrijvingen van foutcodes. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Conventies en conversies voor Go-code. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Beheerstrategieën voor OpenIM Docker-images, verspreid over meerdere architecturen en image-opslagplaatsen. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Verdere gedetailleerde conventies over houtkap. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Naamgevings- en beheerstrategieën voor OpenIM-versies. - - -## Voor ontwikkelaars, bijdragers en communitybeheerders - -### Ontwikkelaars en bijdragers - -Als u een ontwikkelaar bent of iemand die graag een bijdrage wil leveren: - -- Maak uzelf vertrouwd met onze [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) en [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) om soepele bijdragen te garanderen. -- Duik in de [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) om de ontwikkelpraktijken in OpenIM onder de knie te krijgen. - -### Gemeenschapsbeheerders - -Als gemeenschapsbeheerder: - -- Zorg ervoor dat bijdragen in overeenstemming zijn met de normen die in onze documentatie worden beschreven. -- Controleer regelmatig het [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) en de [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) om op de hoogte te blijven. - -## Voor gebruikers - -Gebruikers moeten bijzondere aandacht besteden aan: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Noodzakelijk als u van plan bent Docker-images van OpenIM te gebruiken. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Om de verschillende beschikbare afbeeldingen te begrijpen en hoe u de juiste voor uw architectuur kiest. \ No newline at end of file diff --git a/docs/README_pl.md b/docs/README_pl.md deleted file mode 100644 index 9e89c9dae..000000000 --- a/docs/README_pl.md +++ /dev/null @@ -1,67 +0,0 @@ -# Dokumentacja serwera OpenIM - -Witamy w centrum dokumentacji OpenIM! Centrum to zapewnia kompleksową gamę przewodników i podręczników zaprojektowanych, aby pomóc Ci w pełni wykorzystać możliwości OpenIM. - -## Spis treści - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Wskazówki dotyczące współtworzenia i konfiguracji dla programistów -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Konwencje kodowania, zasady rejestrowania i inne narzędzia do transformacji - ------- - -## Wkład - -W tej sekcji deweloperzy mogą znaleźć szczegółowy przewodnik dotyczący udostępniania kodu, konfigurowania środowiska i wykonywania powiązanych procesów. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Zasady i konwencje pisania kodu w OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Poradnik dotyczący programowania w OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Wytyczne dotyczące operacji zbierania wiśni. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Przepływ pracy git w OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Wskazówki dotyczące konfigurowania i inicjowania OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Jak zainstalować Docker na swoim komputerze. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Przewodnik po konfigurowaniu środowiska programistycznego w systemie Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Wytyczne dotyczące sposobu przeprowadzania niektórych typowych działań lokalnie. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Metody wdrażania OpenIM offline. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Przewodnik dotyczący korzystania z narzędzi protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Narzędzia i biblioteki w OpenIM for Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Najlepsze praktyki i narzędzia dla Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Najlepsze praktyki i narzędzia dotyczące skryptów. - -## Konwersje - -W tej sekcji przedstawiono różne konwencje i zasady w OpenIM, obejmujące kod, dzienniki, wersje i nie tylko. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Wytyczne i metody konwersji API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Zasady i konwencje rejestrowania w OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedury i konwencje dotyczące CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Konwencje dotyczące zatwierdzania kodu w OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Struktura katalogów i konwencje w OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista i opisy kodów błędów. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Konwencje i konwersje dla kodu Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Strategie zarządzania obrazami Dockera OpenIM obejmujące wiele architektur i repozytoriów obrazów. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Dalsze szczegółowe konwencje dotyczące pozyskiwania drewna. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Strategie nazewnictwa i zarządzania wersjami OpenIM. - - -## Dla programistów, współpracowników i opiekunów społeczności - -### Programiści i współpracownicy - -Jeśli jesteś programistą lub osobą, która chce wnieść swój wkład: - -- Zapoznaj się z naszymi [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) i [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), aby zapewnić płynną współpracę. -- Zajrzyj do [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), aby zapoznać się z praktykami programistycznymi w OpenIM. - -### Opiekunowie społeczności - -Jako opiekun społeczności: - -- Upewnij się, że wkład jest zgodny ze standardami określonymi w naszej dokumentacji. -- Regularnie przeglądaj [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) i [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), aby być na bieżąco. - -## Dla Użytkowników - -Użytkownicy powinni zwrócić szczególną uwagę na: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Niezbędne, jeśli planujesz używać obrazów Dockera OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Aby zrozumieć różne dostępne obrazy i dowiedzieć się, jak wybrać odpowiedni dla swojej architektury. \ No newline at end of file diff --git a/docs/README_pt_BR.md b/docs/README_pt_BR.md deleted file mode 100644 index e28db74a6..000000000 --- a/docs/README_pt_BR.md +++ /dev/null @@ -1,67 +0,0 @@ -# Documentação do Servidor OpenIM - -Bem-vindo ao centro de documentação do OpenIM! Este centro oferece uma ampla gama de guias e manuais projetados para ajudá-lo a aproveitar ao máximo sua experiência com o OpenIM. - -## Índice de Conteúdos - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Orientações sobre contribuições e configurações para desenvolvedores -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Convenções de codificação, políticas de registro e outras ferramentas de transformação - ------- - -## Contrib - -Esta seção oferece aos desenvolvedores um guia detalhado sobre como contribuir com código, configurar seu ambiente e seguir os processos associados. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Regras e convenções para escrever código no OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Um guia sobre como realizar o desenvolvimento dentro do OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Diretrizes sobre operações de cherry-picking. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - O fluxo de trabalho git no OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Orientações sobre configuração e inicialização do OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Como instalar o Docker em sua máquina. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Guia para configurar o ambiente de desenvolvimento no Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Diretrizes sobre como realizar certas ações comuns localmente. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Métodos para implantar o OpenIM offline. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Guia sobre o uso de ferramentas protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Ferramentas e bibliotecas no OpenIM para Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Melhores práticas e ferramentas para Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Melhores práticas e ferramentas para scripts. - -## Conversions - -Esta seção apresenta várias convenções e políticas dentro do OpenIM, abrangendo código, logs, versões e mais. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Diretrizes e métodos para conversões de API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Políticas e convenções de registro no OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Procedimentos e convenções para CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Convenções para commits de código no OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Estrutura de diretórios e convenções dentro do OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Lista e descrições de códigos de erro. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Convenções e conversões para código Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Estratégias de gerenciamento para imagens Docker do OpenIM, abrangendo várias arquiteturas e repositórios de imagens. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Convenções mais detalhadas sobre registro. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Estratégias de nomeação e gerenciamento para versões do OpenIM. - - -## Para Desenvolvedores, Contribuidores e Mantenedores da Comunidade - -### Desenvolvedores & Contribuidores - -Se você é um desenvolvedor ou alguém interessado em contribuir: - -- Familiarize-se com nossas [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) e [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) para garantir contribuições suaves. -- Mergulhe no [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) para se familiarizar com as práticas de desenvolvimento no OpenIM. - -### Mantenedores da Comunidade - -Como mantenedor da comunidade: - -- Garanta que as contribuições estejam alinhadas com os padrões descritos em nossa documentação. -- Reveja regularmente a [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) e [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) para se manter atualizado. - -## Para Usuários - -Os usuários devem prestar atenção especial a: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Necessário se você planeja usar imagens Docker do OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Para entender as diferentes imagens disponíveis e como escolher a certa para a sua arquitetura. diff --git a/docs/README_ru.md b/docs/README_ru.md deleted file mode 100644 index 0e89df7c7..000000000 --- a/docs/README_ru.md +++ /dev/null @@ -1,67 +0,0 @@ -# Документация по серверу OpenIM - -Добро пожаловать в центр документации OpenIM! Этот центр предоставляет широкий спектр руководств и руководств, призванных помочь вам максимально эффективно использовать возможности OpenIM. - -## Оглавление - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Руководство по участию и настройке для разработчиков -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Соглашения по кодированию, политики ведения журналов и другие инструменты преобразования. - ------- - -## Вклад - -В этом разделе разработчикам предлагается подробное руководство о том, как добавлять код, настраивать среду и следовать соответствующим процессам. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Правила и соглашения по написанию кода в OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Руководство о том, как вести разработку в OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Руководство по сбору урожая. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Рабочий процесс git в OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Руководство по настройке и инициализации OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Как установить Docker на свой компьютер. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Руководство по настройке среды разработки в Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Рекомендации о том, как выполнять определенные общие действия на местном уровне. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Способы развертывания OpenIM в автономном режиме. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Руководство по использованию инструментов протокола. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Инструменты и библиотеки в OpenIM для Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Лучшие практики и инструменты для Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Лучшие практики и инструменты для сценариев. - -## Конверсии - -В этом разделе представлены различные соглашения и политики OpenIM, включая код, журналы, версии и многое другое. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Рекомендации и методы преобразования API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Политики и соглашения ведения журналов в OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Процедуры и соглашения для CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Соглашения о фиксации кода в OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Структура каталогов и соглашения в OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Список и описание кодов ошибок. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Соглашения и преобразования для кода Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Стратегии управления образами OpenIM Docker, охватывающими несколько архитектур и репозиториев изображений. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Дальнейшие подробные соглашения о ведении журнала. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Стратегии именования и управления версиями OpenIM. - - -## Для разработчиков, участников и сопровождающих сообщества - -### Разработчики и участники - -Если вы разработчик или кто-то хочет внести свой вклад: - -- Ознакомьтесь с нашими [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) и [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), чтобы обеспечить бесперебойную работу. -- Погрузитесь в [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), чтобы ознакомиться с методами разработки в OpenIM. - -### Сопровождающие сообщества - -Как администратор сообщества: - -- Убедитесь, что вклады соответствуют стандартам, изложенным в нашей документации. -- Регулярно просматривайте [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) и [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), чтобы оставаться в курсе событий. - -## Для пользователей - -Пользователям следует обратить особое внимание на: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Необходимо, если вы планируете использовать Docker-образы OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Чтобы понять различные доступные изображения и как выбрать подходящее для вашей архитектуры. \ No newline at end of file diff --git a/docs/README_tr.md b/docs/README_tr.md deleted file mode 100644 index d2bd119e4..000000000 --- a/docs/README_tr.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Sunucu Belgeleri - -OpenIM Belgeleri merkezine hoş geldiniz! Bu merkez, OpenIM deneyiminizden en iyi şekilde faydalanmanıza yardımcı olmak için tasarlanmış kapsamlı bir rehber ve kılavuzlar koleksiyonu sunar. - -## İçindekiler - -1. [Katılım](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Geliştiriciler için katkıda bulunma ve yapılandırma rehberi -2. [Dönüşümler](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Kodlama kuralları, günlükleme politikaları ve diğer dönüşüm araçları - ------- - -## Katılım - -Bu bölüm, geliştiricilere kod katkısında bulunma, çevrelerini kurma ve ilişkilendirilmiş süreçleri takip etme konusunda detaylı bir rehber sunar. - -- [Kod Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM'de kod yazma kuralları ve gelenekleri. -- [Geliştirme Rehberi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - OpenIM içinde geliştirme nasıl yapılır konusunda bir rehber. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Cherry-pick işlemleri için yönergeler. -- [Git Çalışma Akışı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM'deki git çalışma akışı. -- [Başlangıç Yapılandırmaları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - OpenIM'i kurma ve başlatma konusunda rehberlik. -- [Docker Kurulumu](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Makinenize Docker nasıl kurulur. -- [Linux Geliştirme Ortamı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux üzerinde geliştirme ortamını kurma kılavuzu. -- [Yerel İşlemler](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Yerelde belirli yaygın işlemleri nasıl gerçekleştireceğiniz hakkında yönergeler. -- [Çevrimdışı Dağıtım](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - OpenIM'in çevrimdışı nasıl dağıtılacağı yöntemleri. -- [Protoc Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Protoc araçlarını kullanma rehberi. -- [Go Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Go için OpenIM'deki araçlar ve kütüphaneler. -- [Makefile Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile için en iyi uygulamalar ve araçlar. -- [Betik Araçları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Betikler için en iyi uygulamalar ve araçlar. - -## Dönüşümler - -Bu bölüm, kod, günlükler, sürümler ve daha fazlasını içeren çeşitli OpenIM içindeki kuralları ve politikaları tanıtır. - -- [API Dönüşümleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API dönüşümleri için yönergeler ve yöntemler. -- [Günlükleme Politikası](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM'deki günlükleme politikaları ve gelenekleri. -- [CI/CD İşlemleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD için prosedürler ve gelenekler. -- [Taahhüt Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM'deki kod taahhütleri için kurallar. -- [Dizin Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM içindeki dizin yapısı ve kurallar. -- [Hata Kodları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Hata kodlarının listesi ve açıklamaları. -- [Go Kod Dönüşümleri](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go kodu için kurallar ve dönüşümler. -- [Docker İmaj Stratejisi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker imajlarının yönetim stratejileri, birden fazla mimariyi ve imaj depolarını kapsar. -- [Günlükleme Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Günlükleme hakkında daha fazla ayrıntılı kurallar. -- [Sürüm Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM sürümleri için adlandırma ve yönetim stratejileri. - - -## Geliştiriciler, Katkıda Bulunanlar ve Topluluk Bakımı - -### Geliştiriciler & Katkıda Bulunanlar - -Eğer bir geliştirici veya katkıda bulunmaya hevesli biriyseniz: - -- Katkılarınızı düzgün bir şekilde yapmak için [Kod Kuralları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) ve [Git Çalışma Akışı](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) ile tanışın. -- OpenIM'deki geliştirme uygulamalarını anlamak için [Geliştirme Rehberi'ne](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) göz atın. - -### Topluluk Bakımı - -Topluluk bakımı olarak: - -- Katkıların belirtilen standartlarla uyumlu olduğundan emin olun. -- [Günlükleme Politikası](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) ve [Hata Kodları](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) sık sık gözden geçirerek güncel kalın. - -## Kullanıcılar İçin - -Kullanıcılar, özellikle dikkat etmelidir: - -- [Docker Kurulumu](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - OpenIM Docker imajlarını kullanmayı planlıyorsanız gereklidir. -- [Docker İmaj Stratejisi](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Mevcut farklı imajları anlamak ve mimarinize uygun olanı nasıl seçeceğinizi öğrenmek için. diff --git a/docs/README_ua.md b/docs/README_ua.md deleted file mode 100644 index 1eaf6b5d1..000000000 --- a/docs/README_ua.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server документ - -Ласкаво просимо до Центру документації OpenIM! Цей центр надає вичерпні посібники та посібники, розроблені, щоб допомогти вам отримати максимальну віддачу від роботи з OpenIM. - -## Зміст - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Посібник із внесків і налаштування для розробників -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Інструкції з кодування, політики журналювання та інші інструменти перетворення - ------- - -## Посібник із внесків - -Цей розділ надає розробникам докладні вказівки щодо того, як додати код, налаштувати своє середовище та дотримуватися пов’язаних процесів. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Правила та умовності для написання коду в OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Посібник з розробки в OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Ретельно підібрані інструкції. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - робочий процес git у OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Посібник із налаштування та ініціалізації OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Як встановити Docker на вашу машину. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Посібник із налаштування середовища розробки в Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Посібник із виконання деяких типових операцій локально. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Як розгорнути OpenIM офлайн. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Посібник із використання інструменту protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Інструменти та бібліотеки для Go в OpenIM. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Найкращі практики та інструменти для Makefiles. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Найкращі практики та інструменти для створення сценаріїв. - -## Методи внеску - -У цьому розділі описано різні практики та політики в OpenIM, зокрема код, журнали, версії тощо. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Рекомендації та методи перетворення API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Політика та практика журналювання в OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Процедури та практики CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Конвенції для подання коду в OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Конвенції для подання коду в OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Перелік і опис кодів помилок. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Конвенції та перетворення коду Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Стратегія керування зображеннями OpenIM Docker, що охоплює кілька архітектур і сховищ зображень. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Більш детальні умови для журналювання. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Стратегії іменування та керування для версій OpenIM. - - -## Для розробників, співавторів і супроводжувачів спільноти - -### Розробники та учасники - -Якщо ви розробник або бажаєте зробити внесок: - -- знайомі з нами [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) і [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md), щоб забезпечити плавний внесок. -- зрозуміти глибше [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md), освоїти практики розробки OpenIM. - -### супроводжувач спільноти - -Як супроводжувач спільноти: - -- Переконайтеся, що внески відповідають стандартам, викладеним у нашій документації. -- Регулярно перевіряйте [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) i [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md), щоб бути в курсі подій. - -## Для користувачів - -Користувачам слід звернути особливу увагу на: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Це буде необхідно, якщо ви плануєте використовувати образ Docker OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Дізнайтеся про доступні зображення та про те, як вибрати правильний для вашої архітектури. diff --git a/docs/README_vi.md b/docs/README_vi.md deleted file mode 100644 index 55a99b8e5..000000000 --- a/docs/README_vi.md +++ /dev/null @@ -1,67 +0,0 @@ -# Tài liệu Máy chủ OpenIM - -Chào mừng bạn đến với trung tâm tài liệu OpenIM! Trung tâm này cung cấp một loạt các hướng dẫn và hướng dẫn chi tiết được thiết kế để giúp bạn tận dụng tối đa trải nghiệm OpenIM của mình. - -## Mục lục - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Hướng dẫn về đóng góp và cấu hình cho các nhà phát triển -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - Quy ước mã hóa, chính sách ghi nhật ký và các công cụ chuyển đổi khác - ------- - -## Đóng góp - -Phần này cung cấp cho các nhà phát triển một hướng dẫn chi tiết về cách đóng góp mã, thiết lập môi trường của họ và tuân theo các quy trình liên quan. - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - Quy tắc và quy ước viết mã trong OpenIM. -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - Hướng dẫn về cách thực hiện phát triển trong OpenIM. -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - Hướng dẫn về các hoạt động chọn lọc. -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - Quy trình làm việc git trong OpenIM. -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - Hướng dẫn về thiết lập và khởi tạo OpenIM. -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cách cài đặt Docker trên máy của bạn. -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Hướng dẫn thiết lập môi trường phát triển trên Linux. -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - Hướng dẫn về cách thực hiện một số hành động phổ biến ở cấp địa phương. -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - Các phương pháp triển khai OpenIM ngoại tuyến. -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - Hướng dẫn sử dụng công cụ protoc. -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - Công cụ và thư viện trong OpenIM cho Go. -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Thực hành tốt nhất và công cụ cho Makefile. -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - Thực hành tốt nhất và công cụ cho kịch bản. - -## Chuyển đổi - -Phần này giới thiệu các quy ước và chính sách khác nhau trong OpenIM, bao gồm mã, nhật ký, phiên bản và hơn thế nữa. - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - Hướng dẫn và phương pháp chuyển đổi API. -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - Chính sách và quy ước ghi nhật ký trong OpenIM. -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - Quy trình và quy ước cho CI/CD. -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - Quy ước cho các cam kết mã trong OpenIM. -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - Cấu trúc thư mục và quy ước trong OpenIM. -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - Danh sách và mô tả các mã lỗi. -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Quy ước và chuyển đổi cho mã Go. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Chiến lược quản lý hình ảnh Docker của OpenIM, bao gồm nhiều kiến trúc và kho lưu trữ hình ảnh. -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - Quy ước chi tiết hơn về ghi nhật ký. -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - Chiến lược đặt tên và quản lý phiên bản OpenIM. - - -## Dành cho Nhà phát triển, Người đóng góp và Người duy trì Cộng đồng - -### Nhà phát triển & Người đóng góp - -Nếu bạn là nhà phát triển hoặc ai đó muốn đóng góp: - -- Làm quen với [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) và [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) của chúng tôi để đảm bảo đóng góp trôi chảy. -- Tìm hiểu [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) để nắm bắt các thực hành phát triển trong OpenIM. - -### Người duy trì Cộng đồng - -Là người duy trì cộng đồng: - -- Đảm bảo rằng các đóng góp phù hợp với các tiêu chuẩn được nêu trong tài liệu của chúng tôi. -- Thường xuyên xem lại [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) và [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) để cập nhật thông tin. - -## Dành cho Người dùng - -Người dùng nên chú ý đặc biệt đến: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - Cần thiết nếu bạn dự định sử dụng hình ảnh Docker của OpenIM. -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - Để hiểu các hình ảnh khác nhau có sẵn và cách chọn hình ảnh phù hợp cho kiến trúc của bạn. diff --git a/docs/README_zh_CN.md b/docs/README_zh_CN.md deleted file mode 100644 index 413d5dfa1..000000000 --- a/docs/README_zh_CN.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM Server 文档 - -欢迎来到 OpenIM 文档中心!本中心提供全面的指南和手册,旨在帮助您最大限度地利用 OpenIM 体验。 - -## 目录 - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 为开发者提供的贡献指南和配置 -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 编码规范、日志策略和其他转换工具 - ------- - -## 贡献指南 - -本节为开发人员提供了如何贡献代码、设置环境以及遵循相关流程的详细指南。 - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - OpenIM 中编写代码的规则和惯例。 -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - 关于如何在 OpenIM 内进行开发的指南。 -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 精挑细选的操作指南。 -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM 中的 git 工作流程。 -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - 设置和初始化 OpenIM 的指南。 -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如何在您的机器上安装 Docker。 -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - 在 Linux 上设置开发环境的指南。 -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 关于如何在本地执行某些常见操作的指南。 -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 离线部署 OpenIM 的方法。 -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 使用 protoc 工具的指南。 -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM 中 Go 的工具和库。 -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile 的最佳实践和工具。 -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 脚本的最佳实践和工具。 - -## 贡献方法 - -本节介绍 OpenIM 内的各种惯例和政策,包括代码、日志、版本等。 - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 转换的指南和方法。 -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM 中的日志策略和惯例。 -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 的程序和惯例。 -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM 中代码提交的惯例。 -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 内的目录结构和惯例。 -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 错误代码的列表和描述。 -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 代码的惯例和转换。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker 镜像的管理策略,涵盖多个架构和镜像仓库。 -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 有关日志的更详细的惯例。 -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 版本的命名和管理策略。 - - -## 对于开发者、贡献者和社区维护者 - -### 开发者和贡献者 - -如果您是一名开发者或热衷于贡献: - -- 熟悉我们的 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 和 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md),以确保顺利贡献。 -- 深入了解 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md),掌握 OpenIM 的开发实践。 - -### 社区维护者 - -作为社区维护者: - -- 确保贡献符合我们文档中概述的标准。 -- 定期查看 [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 和 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md),以保持最新状态。 - -## 对于用户 - -用户应特别注意: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如果您计划使用 OpenIM 的 Docker 镜像,那么这个将会是必须的。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 了解可用的镜像以及如何为您的架构选择正确的镜像。 diff --git a/docs/README_zh_TW.md b/docs/README_zh_TW.md deleted file mode 100644 index 8ca00ba17..000000000 --- a/docs/README_zh_TW.md +++ /dev/null @@ -1,67 +0,0 @@ -# OpenIM 伺服器文檔 - -歡迎來到 OpenIM 文件中心! 該中心提供全面的指南和手冊,旨在幫助您充分利用 OpenIM 體驗。 - -## 目錄 - -1. [Contrib](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 開發人員貢獻和配置指南 -2. [Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib) - 編碼約定、日誌記錄策略和其他轉換工具 - ------- - -## 貢獻 - -本節為開發人員提供了有關如何貢獻程式碼、設定環境以及遵循相關流程的詳細指南。 - -- [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) - 在 OpenIM 中編寫程式碼的規則和約定。 -- [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) - 有關如何在 OpenIM 中進行開發的指南。 -- [Git Cherry Pick](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/gitcherry-pick.md) - 精挑細選操作指南。 -- [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) - OpenIM 中的 git 工作流程。 -- [Initialization Configurations](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/init-config.md) - 設定和初始化 OpenIM 的指南。 -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如何在您的電腦上安裝 Docker。 -- [Linux Development Environment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/linux-development.md) - Linux 上的開發環境設定指南。 -- [Local Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/local-actions.md) - 關於如何在當地進行某些共同行動的指南。 -- [Offline Deployment](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/offline-deployment.md) - 離線部署OpenIM的方法。 -- [Protoc Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/protoc-tools.md) - 協議工具使用指南。 -- [Go Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-go.md) - OpenIM 在 Go 中的工具和函式庫。 -- [Makefile Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-makefile.md) - Makefile 的最佳實務和工具。 -- [Script Tools](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/util-scripts.md) - 腳本的最佳實踐和工具。 - -## 轉換 - -本節介紹 OpenIM 中的各種約定和策略,包括程式碼、日誌、版本等。 - -- [API Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/api.md) - API 轉換的指南和方法。 -- [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) - OpenIM 中的日誌記錄策略和約定。 -- [CI/CD Actions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/cicd-actions.md) - CI/CD 的程序和約定。 -- [Commit Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/commit.md) - OpenIM 中程式碼提交的約定。 -- [Directory Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/directory.md) - OpenIM 中的目錄結構和約定。 -- [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) - 錯誤代碼的清單和描述。 -- [Go Code Conversions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/go-code.md) - Go 程式碼的約定和轉換。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - OpenIM Docker 映像的管理策略,跨越多種架構和映像儲存庫。 -- [Logging Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/logging.md) - 有關日誌記錄的更詳細約定。 -- [Version Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/version.md) - OpenIM 版本的命名與管理策略。 - - -## 對於開發者、貢獻者和社區維護者 - -### 開發者和貢獻者 - -如果您是開發人員或熱衷於做出貢獻的人: - -- 熟悉我們的 [Code Conventions](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/code-conventions.md) 和 [Git Workflow](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/git-workflow.md) 以確保順利貢獻。 -- 深入閱讀 [Development Guide](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/development.md) ,掌握 OpenIM 的開發實務。 - -### 社區維護者 - -作為社區維護者: - -- 確保貢獻符合我們文件中概述的標準。 -- 定期查看 [Logging Policy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/bash-log.md) 和 [Error Codes](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/error-code.md) 以保持更新。 - -## 對於用戶 - -使用者應特別注意: - -- [Docker Installation](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/install-docker.md) - 如果您打算使用 OpenIM 的 Docker 映像,則這是必要的。 -- [Docker Image Strategy](https://github.com/openimsdk/open-im-server/blob/main/docs/contrib/images.md) - 了解可用的不同影像以及如何為您的架構選擇正確的影像。 \ No newline at end of file diff --git a/docs/contributing/CONTRIBUTING-JP.md b/docs/contributing/CONTRIBUTING-JP.md new file mode 100644 index 000000000..86bbfefcd --- /dev/null +++ b/docs/contributing/CONTRIBUTING-JP.md @@ -0,0 +1,33 @@ +# How do I contribute code to OpenIM + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + + +

\ No newline at end of file diff --git a/docs/contributing/CONTRIBUTING-PL.md b/docs/contributing/CONTRIBUTING-PL.md new file mode 100644 index 000000000..86bbfefcd --- /dev/null +++ b/docs/contributing/CONTRIBUTING-PL.md @@ -0,0 +1,33 @@ +# How do I contribute code to OpenIM + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + + +

\ No newline at end of file diff --git a/docs/images/Open-IM-Servers-on-System.png b/docs/images/Open-IM-Servers-on-System.png deleted file mode 100644 index 3c8a1020214a2cfcb9e5cc6c5beb339b7e2be67d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21614 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 Z7iVs2wPSi}fqg{rG*ony3lwkr{y(Ud)0qGO diff --git a/docs/images/Open-IM-Servers-on-docker.png b/docs/images/Open-IM-Servers-on-docker.png deleted file mode 100644 index c66f7fb095e862e64eaab2a9dbc1ac1c3c73977b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10172 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

Ul4l+_q-iF9P^FloHd81J@_?< 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

m+-H`IeBmCUU{1v4gb(;8fSe)oiB@Myr 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�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.|E?p#$-*f^W>vqcD{I zw8;i3byoOkl6J}UX4(Sc?Rw_+p0=AsCZtbbEpxh9H2-wDLA7HmD1_@^zi~}13 z$_!k!j@k7lk};9j?~B7*ttIQi^0 z?28!xIfKuC|C|8Ad{pxDeMCWWvc<9i!QeckFzhM-Bq;p=HQ(K-7C20UK~yyJcF0-4 z)I(i1YVl$!<<&7F!8h4PKO^HHje#mTH{0=^<>&VMdU*>c*$k@?w0IYxS^hmCRNN

`W{JVLM(s9>JC39zFIRIbt`tM22~W3VyclDIuzHzaZ`}L|8%IaX{Rk%%dO!ro zT8=x}W`0mF_cV0;=U($X(+9*7UYv8d3pu0PVgIB@@Zjsb7JhXJ?l%arvcw>hetJ(x z)K6V2{x;U?<>?IBk# zIjwZJEl!Cl=fY2i=bgcx03-DgN#Cp~Xu5{IP3og5X1KFWQ40_ zS1%%R{)MSl>Mt=lYo>98*Doj*ob$aJY>FFH8_ziujQ&hbwBl>Ru}F}yn=Tjk4X42j zJ7@-Nzpu6%-Bcr{dp7R609_IA;)Hi>^Xg@>HES!UzdOGpxl?m14zr-Zk5HGW?z{2e zs2u=T!K^7Sh+<@s%jUAuR4AM(HXVNau_iEnmfN#`*J&0v`d^?iAlW}3U~M(UJ>hd9eYxgxv8!BAT8gjfCTrajlKKItH5Ua$>(SLB zTZT?>7wW|K>=`K*FSpgAy>fC#!eX(6!|{p~Qavoi(pgX=$=5cbHgonojH3j#L_J>! zThh=~G&GZwoWKv-)6A1H&I9oms;#RiL2#6sHOHP+(RSMdh6`>ABZ}NtqRO`QyW*o@ zmu>E2pE%DzwK05a+Hfn0=XN~_R=A7~Ue+f2=Y@-xvrms#LhYKbH5IJAZ+B=5N=ooR zP#}kxV$N-XHUtD9Mz#XH2aMfD`9heNH!NI|apwwQLvvh2xfB}RAjxt$-SpduiJw94 zL`~Ik)+w1sJHBeXSH*+5&2M6;lMAiV(&uGR+fjD+%5xyj4Hl?AqjyL$$VNj=xebF> zXGxrQODg8#xccm;7YMVw&S9 zIl&c2563T8@#!81rF%Z#>fd}gNwSmbFr7-lco%xegY+iIM9=J1il6&fN%?Z?M-FGx zp1K>aJ+Mt0U-u>JRs_PQ(#U@2$7eg6HKUl_rPx!u)VY=~t-grDH1esmjBp zrsLza+lw{<obet*GFZhjKp0-t+zS_)MbIb1x2$%lz4Hv+uI;aI*S zaDF)~%pgf=q%vXn@&jv*(?d6^s65<3%^neG!@H_>v(zux7MWE#)yy~-5py9v!KalV zs#`)NZqVW(#gB857XIfb?Jy9bM<^X*i?ilz;7?|+Xg^QZPVwr zO!LL~LUt+qQp`G)l~f8?t&TF!U6)M*vRoWxz-q=-%4IlNj~oO&JXX#Na-6pLOoyJg zDR2-pukU5G2PTTR3VTgH{VF$iMUHl&(O%2T5kiRn0bs>Tv#QegyXUbAUsk}{6TglHT`YxSz4Lp30$ns{Kk# z5KKU9xy)M@=n(okRqAzv-VcTSkgjdx$rdN^m47ss>=!w=1wpa%C+aGZ8eQu^xnu8u z3hQQ1`qWtup)BiWzPri!yRpMx&W9+s#_JxFCXhw}Jz2|KXDX=X)IVu=lED32G;cPF2N};=( zHF`CiZM7)9KEjv=#J@c*_MK7UF_Lsnpd}8u=KJq&eLb%BWK*-7r6B0XEpDsHw4FE3 zr1O{|>YEPPKT%xIt|J70bHI_2f)KDr^CrL@l`ta4^EEbNZ|_$ZeV`vA(h%9P$tW`A zQ)xY3vSPRfHr;fcEmCUnsBm9EC25&#=;Ws?}Ea7zfL${jlbJ-4-Ohjyb06XoB zDY$A5&hVrx#b0LNzfD{0za89{xzTeQ-%CZu)?q#l*qJLFo8O#Oy+d|ATWgeiDU2w5 zI*KFk-Xn!zEl~hjhsG2dF(!uzK>%U!$EA2=w#qdw#c_W4<#)fD?^oeE9hlmZI_G3c zT|y5tyb+!G4e4X9b*M0!deRCOoDI%RSOrE3*;H(U(5Qd+*ZFOusH#S+-Btc`q=R^u zYlGi6CX(l89(cP^4-lXcEA6m%D1}88?%S6~*HatqOD5|n!bdIVzYWK>RA~hn4<#2} zk?!$GEGE?p-D&U8j{=|_nkW^2U?UY}n>Ov@ozfJ5>pElNCvHDHG7^^T1po|h>VxeE zK)Hl==0#qHFNWsYVJ%lbX_|@Vm0Kgc~w4|Pj^!jP2iR9HF zXdiSxt7b@I(`|ltz!bi(6oDZ8kwPugG%oz5k>`B^~k4i zBfRluGnU&9Ey+RG-ES6}|7h-L^ z&b~_ch6G|YUstb@LHhwHpDSLqXNukIKl&x`Yfh#aZ_nh4dwgkPJYuV4-Q?K!Gx>Vx zKaFMxNhO-p`++~6wobZ%{rViazs7t~(S?V2uJU^FwH^5(0)bKumP`$qdq!@aB(kj` zVI~2%0t2L`IO27v#CoX8D$>E@S^&CDU+m9T!CqO>p0w(WeEaL@Pi^8Aq46^52xpLw zf%1{zay#`08|3s~q&7pL989^LKb1y7CU-36V>Kv?iw!tmmhcO_`=jyWYUj*2Y!}Pn zc-HDmfmE}gpwMi%N&NgEWR}yVUB91^?)>f};mS9xm2;ga8!TCP3|maetviQuoY5p%dC8-%PaoGU)y6nio~Z~)I7ILdj+1pMuj@M^5#29fYU7}od#qge>G>*) z1Dr;2qWWcPceyh`2 z*D|)Ww@vD{=$kMp=luhB6x6>IJT`mxKH!AywZoFQ0_G6b3hNAMp!2vXYZ0gEN(yo| zc`xR)p!Bcq@cIpnIC1{TF46GBZU~WQtn(1T@{}c}h>Az!rs*$Fu>0-GX z+_6^}Kws?ij;lSK^t(J1%Hx{#m7!*_Sf%+qg^1w_*@i*j8@|%f#N>b{SrV-s!SL#v zc^azPsz19yUZ)rtVzt=D0+%jIJ>-<4rI2nVdrZ$Glw#JgnCuat7y zU-%xjoa^>6Qpi8rgg@q_9iW2ODPoj&mC2D8!8&QrpK;cZ`knFlM7$JF(n&Je+n zFc~SvbhTIlZ;L;scqpl~Qm6R13Z4pfI<^}S+7>Lzq$J%fU!IKZhvXQb9r54 z`b<9EAc+ebRw?yM5{_!Xk`#Lhdny`(xGTIkj>0N{uI87exXn=q@*Wm_Tqj&y-CT#c zz2`uvZ^5O;IkZ>Ld@|Ht8R~p!LdDDcI~|sC1>?7o5+w~q z{4pm+n5qcStiTs3fkl$;Laxodysz7Q>{v$nm|+>=u+NES=Aj8#Z6cTkZmkPc=qS7G z_VmCh#LB~;#w?Xc>4>BdM!3MRQ#2&B%>G&xv(j56rvXVDiMk3lDe#%=yrKRe%2Kt{ z-s6rLQ_yEZrGqle2zS+QF$0^m;$8+aanJE4*Hb6vPm^a|Bl^Na!- zXwuh3m*T+r}WK_BVu0z7f`%f+(7^ z-2j*)d=$3#_x^C~@@?{+3Wiq~!9709!<^B)d0L@XmRIS2Q|`CjwQP4-k37ATD-sdi z+jGgm+Kwe$wl8fe4{9?P0y2nTltY%Ne@)LZ4;Vl)bGYAA#gY2XQPwT0pzH4&ygu0& z^FHlmFMoqW=po_WpSKA#sCs*kl_>7^iKu?Jt(DHtNcq6~Z2xDwxsjehR@Ia*QGdgx zARNg-OXn#)ld5Pd#XR=LJNr|VH8qTg=F2{Z22id^YIhwUO1Ss#L{Hvy9!A#HP;H{; zIz_rSpQ9OV3O)mb+DUY8(e4w*3Vf!EV(rjwz9q3u^+oh%q?-K~2+n+WE9vE{X+oW3 zJxJ!4kGsi1Mi?Yt)sj^MMke4#*Ig<}I5`*_{0ONzK39T5y9Sm0YPw}X2HPReyG3k( zk<^)Re_|-q^uUa!3&M9Qur*|jOcN&61Wf055x=zRey^x9kPg8qKkCsPxwV!G$8I6z z(%C6*h*5TV_?g5pg`!NJp$D85a8M~AxELgzqYCbVebD|mZtNAkK3oWTOa!H11XCb0 zMFQ%#qD>z^&h1YqM>`FKwke$8DreMb6u z?2skGi{uLHC5$7ruG26yH=r)Ow0zk1+h*|;3_v#m2&O1T5|oP@M`f_8#&P>Li^N@D z?8~Hkk|lFVb|O>_czqqoBeJM(W5S>ur!5ZX((p0M_4mMop#+i+p^z}a0tDR4_05R( zZH>_zN`l?~wu(F@qWTYaA+EGXA3YRY{rE3VJ2F^08x*<++XdcM{1hb$5ZHbHH8LE( zS;WTck>ANX4TbVuT}3mn5(*&nwG9fc!Qdl7De^br6rC?b&FvvXSvsmYJ7m0D^pu*N znS>`N!U|-XOx2KsAXgw)Rf4b)f?&W-o zyNJ%V^3%hA{6j-A!g7|D>wp2{9;60{Zoj0ZA-?=d@^|;05#EBsoG_9Iq5V7E>O=5& zc$CAWfL_5)R(Ue6aRc;US^$wG88lVXnG)=V_6(#@^0sn{aLuUXDwftayM|vlx96U5 zcwlMAQh)>ha}%3^6cP)oCk;I${U(B*Ng`yJ>Ra1s9_#&9)oeq577Z48z@yA_`v)0( zTgD$jf;kXE|LKe*k&TnIJ+&+FHm1o=S04bwbpKjVH10ixmk$V0lO}`%gWCEyH|a3w zkiz4RYB-J4eI=t$$@Zxs)q`IC9u1C1E6fIyNDz1IH+?nj126L%v!N;u{I*3+jaY5% zd>UhX`SSoE5u)tP_?1MB7C^hE)wmOK&#F83QJ#&K`$`hOQ-Dh85N2y;k6eLOS6a9M z>53&wNjhGB$rdwV1&9g6XZ^81o%^a`N9&Vg*Kd(Gd6xsHzZrr9r|?D}D|ivXM7k7k zNo_c9Qa)FNh^E86?JZwGWc#!tU|`r*5UQ0$fb@DaC^O>^SJr5^gWd`&kI*a%YE}Pf zeJS!1T?Fn*Z$+>!Q9laX_p*?e5i%?`cVA~ezzm)1Z=@YTNgv}d&pxoeYtj))6EG!) zVC$vn)a#9Cx?Ks6=rymGvqyv-Uu^BXfJ^H9hKL%Rk-NN(z0&lwO6z!+d=<1jZ_sTK z7u=p4E$886Q0 zi7?5FjPt~b1xX@nNVM?Dc4(m<{(2THObHS}f?4Ha#>lsLJ6_tAecY*DJk0w!E$hPU zq>j4;8TQtVlR2x*~oM-+7mQw4v92oSx$PbC``*lx^{-tC)?|1K~Y z1T=jU50;bDhbR$vdeYtn)h=+CmjIK%#vwADMgGdd-k=94LB?o4OOO5r^{AGs)EDd= zro||{V|5-*-QwM`6?bIHyuCIhT-0N`wsp{7URh#cJToh$b#ICoM6de`3cJD*@8Qh6 zHx&YSf;(BPM#T75OnHL#Ff2m?nHHwZG6)3ZOBCE3v`}^?mwY%NYyu7h!3s%9QEsEspi$7De#{7<;QN!c}*G@4YgW4YM z+A^FGs-)ZU#UFSqvv?Me*Wn1IoI5Q4M?l_L0+=@B@CPdBT9c7mp;Oymls<0dTZ7mT znVgW4$T8~J+3%YHEu`l9-1iV-brWe>+=r!%s~$Hh?XjBL{Hw}pWB{rC&NWxoMR&dXR^tv*dV^&5Adh~Sgt3?k1 zr;HMS_2ArbB^0Ph|9+^JB=aJxXc~_ZI?pncdFB*a{uMoD8U@frxjRuz{F%E?D1$J} z%B3#=_7lKt7k?1THPj#dhnxCa67g34MRsw!lln9umZ|Iq6y#r~v=VMFTsR$u`RzA1 z3qNt)UQFu(uc*iHlVLpf$ncu3merz_dj#6uOpcBU3q71i@oz5LW3IOafnJRBHMe2# z>&1W0#l7L=C|tnc1?BNKm5d&d3AlcFyf^YS85YQ|hZi1qQWg}5&=G!4S<{NUI4w<( z0T!Hr-zO)DArsJyobMiDyBjNE3Dj}vR$U3@v)n?d@D~&IUpc{G#(THaSG1;XrqSo# zZ^EjJ7;DH3>MSX&(e|hjZ7Fe#1LglGFgz?xSRhcth}=t+Gs;9o~s zgbTLr=sz+6eM#UZZ_lf!Kxeg5j0i)}XRJ~mu3DdPo5U+{H)~p2QWzK*I7eG4?yJq z6sp3V;(^EG=#i`)NY|F&Z_AOw+{Agc6eNRLRI&Bf;vPV;yak_3^j_i$|1cEz?#ZEF zs|hd5MNJK__4ufTVDu+GHb35-TA};!Z)XizklX2q=g7Z@{HJSwQ!?(4dOjhJ$GaZu zJJj|cp{^+WR+!-gDZCP!8`XQ5U?ic1*ETUg!NWchIWPk$j3hlld&m!$%h)#|NW4$}9IIwF?E~C_nahgBnntoM#KwjXj zR2`1RzxDaQ%8Reh%8QV9w}*8Yo1a-$ocAuZCo`<@8ycNxe~;hDKPVZKcQ?J$*;=_L zTlJwVr!xV9)Z77aWkkkgXE;vAuo&?7Z4v!b?R z1#b-;7ScrRDcQ3!!Bp=ZVptORxQ0#b|43b|0Y{^?&H1Zn63`82d%hAE87R91xGM5=LP%88TmM7z#j(3q z!PS(k<1<(N{}k9fOKL{*;Nqe@`3!2@rE@(7$-phnui!9ZB+Gc8{jdV$Z29St!FN|L zbjsWNLD_|(+#QH8o&sw+v}G1ma62~@3m)VB9Sh!E0^f=cydwDgt!hmWSCaU=MLxbd zZWXvU_+R?P_p(UG@ee(qp5-v}VUpydPg-t(9PL>QO!}cH1}J8DN;jP59$g5I zK7YakufJS#)YTRSWWd|@sH}DtfD)kbN~5FcJjQ_I^lc>4%*E5@bR>^4U>4iqhPzu;6^h+_R$-8G}mYfETr275!|ENU~zW?y7 zeBaM|UoCaroOs^zS6TE=n9E{FoeBiGVYvBHKSe2n0sk}w3S-b?Ups7zdh^O^m6zyl z>RI-oFf6O{;i;|>t=e?nA1|9FGr95VG~DRF>(T&a8Z9s6hIGsAsVNujFCc1}>OVzh zKumYLY4(?!hcQ^;yzrordDJ~6xP;4bVs#+)vI362jufF#DRXaq{Kjim9=5JxpAQir(uooHA}FLYWeMT9aID{io(g9OMLeJFCut0C zgJ0KvVM4qA=SFrGqTdJV5Z{!zGXNF^j^x3Oe;#~7PyAQyi)zc{_vM<n4-g2;~Spo`6Gcpv2(RK&U8d2Oy?c?0#P?{vm`wo2-kY7GvQW2zVjI-PwN zBiWoszIh^8!9pMS4k%a}StXid?zfo{=D|+p1f6@6+5Gty>>C z%+OCR;3!{t-|p$r<#v9zgVWP=kqWj~tGr)bP6_rBa%#%w@5W_zBjd3ZPU?yCV^>UZ zjbSRC?8rpARML=6xNeanKkmpeL1F49+ zmjxDO@Mnh=v_o;yJQSx>(DXeFP&5MMY+pb1i?a0vw7Wh!My1!?JXN~j9V?K|lb5!r z`qX^Ty_;)szk9UOMPocXEa_TYOom66wGAYI@wK49q(r6^{Xo&4jCVx=a1KxR#_@@> zjgf{fN5(E3Uj?rmj)nzXj1LDF`jS|oNwf8S41e-ugz)M6!ptU&>4M$q9EP4Od&X(y z;tR4e$+W!2Ut`b+@Ukaq)Eu-O*4N7^j#KK^z2}J$bY6g`A?=q>MAufkb*=p&&ATD< ztMK=DU^qc>me?vl0rSa_3vf}B_r?}#MP*}<@PGUns6UynDHkKCKS@ACda7*k$7HAZ z4(;=J`i9fN81N=0al8V{hQ%M7!SP1NqwL;PaDwon$Cbg3Um&XLIOTGg8DmV??HV^a zofRF1)wK7hJPqkpLbc_TLuuc~b>rKMA=x$gx1iRSDz_)76qL)tjLx{ZqPwR5|isaY*v z+Wiz~fwtVSDlktzJn`+%>?fV3q?G$kDJ3=cU@p=ZZ+)hn)(kOX!YbT|hLv(Y8tn{e zRA(QTVQwy+6IO=w%Owi>WpkT9()tPUv&xOQNo<-7PHs-5Ny)s#qu9z2mkH?~iC^y< z-{Z8D74cwS*$TpC1b&pvotkZMkNC0-TNvi3z-!qb1+SvY+=*y>_<-;jE21s#eyyv) zT_s;VyvAlRqY48Q!*;Vd5HrClF0J5i?(~Aa$x|LL79VDyKbjHQQ>{e$(;PvW&QGM7 z%~LASiN=ArxO7aeB5onu+o3Zgwe?e~ypxNQbfJC~{j<>4Wg;5yHcS?b7u{zkx|IRa zF{RStGOASMsi@!=AAWC&yP}o(v+2~mcRSUn*62fWcpHn9wRvN+fGLbyFqNVEL?^bb zl!AWUw<%87Wqw#(w@cj!~KmzgZGo5cB@zN_{2o$4}z@b-%8`-<8(AM z{)$RUZjttQo|KzhYhgm8+)1rstv5-PL`$~UeoYa%NgG8+9v+`C$Nl@=@bqeQnAW|& z!*F{sX!)v?o+{!UQr=xMptv(#=N5ouD~0AOyB@RpfWpsL`Z}uj7!Ct9r14Gjoqf{0 zhxjb&DcRwq(44Fu3DT^o11AvDOYRXQ6Li}EWPogWlQ0(#3swdy{nU16qT!Ot-EUM^ zLAf@FZBo+Tb6Es#%iPg%`^7!Y|JeL~z75424r^*u4&v_{VLp7nuo@#TEhD+*VB6T|v z>~XtHs%L7p&fI#fkz1@*KAv~KP|pids`SD!9_l*t6(>1Wfwto|}jS#wFksNv{8@ zRtB=oKxf!#5p4O!=6{12^G-{U;U1!x+FSOPhIEP(1n!bneLdEIVh~-MOV*}d8`l51 zpdB>4`-kw1rG=EGRT5*xG7Yd|V>YNOMW6eDRn&}jR9jIlD1kecsf4wyvCb~2hKD#% z#Sbx-nfeY4O$$BBk^zrOsjr`K{WO893w?kXEH>Ql`w=VF{zSyK{Cj>^f@}e%mE_x? zqH$;Y9+zF}8VIRGtyf87RqZP9cgi9`Va6i#@PD*4vEE7Q-nwE~;Yi*O^JZk=q?PpQ->PTXP3 z_6=D+>$vx`9qwu6HO+XkU<4;<^R3wX+jD5u+Imj}pfQnNY@Pxi1AOET{X|`Zg`T{~MBdfq3a@O;2nhtb_N7+e?a(7&*#HaG_ikhkVKq>qQ1r37%uNj|-%&C||9 z-E?A6)v0n@m%!FGb1eB7aLNlzl}qRSK%n1Xbf3HqG-y+t_z%lxEZcPUU(ikc`!_G~ z#_nN;zBV<*JE0o7YGyL&klT@v z1${$_#&BQ^!hxg=`%9~mV||v8l)y|wWiom%Lwt@fqFZ$=!dGPsqz=(cZQy08SFTJ_bCqpViSio9*e*?UV5eJu?&iopXOKE#;j(hQX=!%lu zke`pkO0G!7>juRLQ0Ur&)VZHDRxSr>O>G zP{ip#@|JoxXRB;iY@;5f+{82BrC)qg!%6ra^bmiO^91EEpqR-2Rl3z zd-}TEtD4U_kZtWxC^(Q5WOMj4a9G1Pkm#$fj_x>*W~_Gcouf65Zi3Bi>fdKiYp10) z4Uf^vjFiDmf9MeC6~-G`QQv4k6IemPwZ5yKxBjLvYxniH_rlSV`cxgCnI!{>x@nkCvW{%ok)(QpJvVeQYH(%N*#+0D z)z-v%83QlWUY{8JKG`0lcE3KRL@^wkxK{s+XoP97_XfoQ=SLIt*2KfW;$6kiPSO*Qhm@ax(vub!s%A zn7-_td(`p}&~kHY!EkI$JDe-IgR-a=A!{1e_r=>=m^Vz&|?$=oH=kGX3P`UVcg4Kt3j>vHAhh zu_j5?8C?9R-RPB!h)ODz>~Ws`C?+AVb&u7p4K`Rs-MT!Ox5Q_#d_%Lu;q=h&hlD{_ zX|PhJucW)nJgwjS4G=R5Uv8>&>fLsK-=7Lm}B-ONIHG zP}%c=5upiu-LJAYg4Ylo?*}LM&1lsMN8!untJ87X)cZuA6qaP($kJ(s{B!Wn@%8<6 zwJF_4(((%eVPUgmr}V0~RZjEf@u~3Om_1x??HO^_DX&2gIt6+du_SbC=U3o3HZ8s{ zN%47QC3Hk-H4>LVl3uZZ3gK|kq)ofb|9U8qo3PF8_B_Ac(;#Up8lP1M6Wxy3E~R_q zkZd9!h&VqNeKaEC_!3#1_NtGfJ4TKl8dpT=b$Aej>>=na)4hADB)wbK#|SqY&+Z>w#+Td%RBuev2rKO3 zj|dka^deibqkJj+Qd;TnQ3DiVD!1`OE8qMjiH>oGoYUFq`XrEnGbniS0kWQCmHP)i z{qc1+Lf9Sy@>_?i)6<;D%|gXHfXwV1gGaUtVXDB8l*C#{6nxyKPE?fbcR*GCy#2*a!d}}k;sYy8n9=QTf8svCsEB8R zkT9V)w<9}q^ftT?bmF2(S{dFJ*{Q&Fl0l5%5PqR9Iq;~L_RsYC1cKevR$h~ZWro5K zo8C4v6yv7SR-(t2=@W>ES%NQrD$F%vF(2J{2^ zX)x#C1M*!@1;@~>yYbkP%K#hFVtbIqDu8j_&(sFf=~j`;o80Oot+BcRORB~A@DVmi z>#b`NjBqcn#$}IhZK4X!H+V3%>4)qq@F)93;iIOrunrg!NK#GlQ`T!jM-7I1^di)L zp7)QWs|FPGlqpjs+A^6Ep_$Q)8;9;q+9U@|z&cx;CjM~vK1G1|7O_f!{0bc{tMbZ5 z`uqBtd!5RydKhd{M47Edl_dh7udmpjhK#OOz<>|DAw!4Tn7x^k1HShIRzF2|Nj+wG zSV$U>y+(!CVvzm_jp;8-L$#T&`uTXV8ibuqwa zD|m~cWcncU$(4X7ubNfO2KVuB+1y@Na7mGid3Ne1T^p8&Olv#!f)L(_m%hl26)Fu*x5*g1t0@eKVGv}HXjSPfU#F#W-1__EZi$Q4ro-wWz^_g zbXV}@r=ZH}k|!HrMj(s_dfv*?ryamGbeowGXQeC2+n064mBhvt0L?-VecumwPgmxpJ92942K3zh~Ia7i*ViP|-L0q5>b!W=+l?T$UA{ zV~Eque#L>UA=A!L$}0Un{l+5|8W`E(K~n6MjgO6di(gp>op_Y5&S*E6lakEu2K%44 z)8f39WxtoJY4ya`_VI3C;uFQNF0^)HN0NOwjE7Wuz(Zrl1bgKJB(v{Z`eqNs8P|PB z#vgb}7}-Mw#0`Co)>0h(%>4H!Yg{l)%n9$_&9GlNp0dGquG(2vfjit^u;@Yf#Dibu zu6 zZ2=^@S`(nT%{WBGmZF$*|?^r(9lmzEllA@xdyf| z=#(OJU(U1aD5_15v7&(qTjk~BSX`gN!YBj*=R136{Eum3cL$Hl6}!OPbVP~P9IaC` z$@o?E_{i(FzI5~mLRPUd1a+| z4$tisz2diO*4w~={>r^4?a_wt0oCFT=M3PL9c~&LvY!S6FiaVpg0aB4;qC1xt5JbT z!nY90@%ZVvy^uV><-=@3wv?SH2^~MX&>fvfHrn|eYM8i@nCQeTv!wlO`ALv%H;@is z4TS$%hH2eAo7t)#K`{lwPY=~}ZtlD)6k2v7`J3+`pFWI|oobXPm3f{YQ~Kq*gdd)F z;it~Smw~a{U>5pfS;?I&m&1IWk0w9b^|Q1Qa_T`~_%Xl0m%u1TA{b%Fr)$r1y-CIK zqlH7aqfn@oB{iQc^C3GolW%Py8wGx04A7KwD>JBa-J}RKxG3E!SXTB&GZ1F{k|8-c%=3|VoqfNO zFj;$I;K8z`!C6*}`nYXA#DC5%QYVHo9kw0&#R}))M50soOR^?+tk30n&sKo0h#IhL z_GcWx<)Wt#r!)1YJ3w8Kx$_m<*(%mFj}A={@|L|iTF%{@E>&`OZvdiL=NNH$Id{4u>u7;lwI#&%r-!cj=Z8zqp=BpI8)MVg1=&_9&4@5&SWr(2T?F3S9{%# zfNFH+f(;|PGoBsKN3?-Al$u*bX~=If^f@J-%c#O0g93#`uNNaCP>e(keB!7R!{4~H z4CirSCaT5MB~F^v*m%$p&oX^#>8w}Q5cm?m-ary!l3V>GIV(&yA?yDxOVXe zX`yQF`H;{!VBOS{OL?^4FL97?16lLrEs+=098!7Ji)FrCteMl(;CcJ#PfO09hS1XT z>+4mCF?sMv7^!qKm29d?KhMoErE;NiFmo>vrUWX_Wr+R6`jr&n@o^Te)+Up;+FbGR{^mK8sQQr|O0;mi9QMN2 zb+P64R3=D(Af4{4hG>9l6qET&$mVjcsvH6Zr}gA$8Vhv*n)2avk-AvDZlUofnq~{= zuyjK?`=lAc)t5WJJoSk;(q!L5J2op&Y^w@nEI(%{3L)yit99%#q=*A===2+IdQvEr zT5HpkDo_V7UKOYo=49DuwW>(b6vLbz4XZkYt!(xYs!3U<=1?=Aq0}!7K|OGXN0aUEV}y8lvOlmQ?VWa%=5< z@(s8=*6R&SSihoyfoWcaD;B=Qu0

U_`CNd-;Rgi&+Oro2Ad=H=dfwCM>!olW1u_ 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 U3~aATNr2CTdzz}ncg_9&AJJ5H6951J literal 0 HcmV?d00001 diff --git a/docs/images/build.png b/docs/images/build.png deleted file mode 100644 index 7c5914c82597681eaeedbec895967d8794eada01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62555 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 diff --git a/docs/images/docker_build.png b/docs/images/docker_build.png deleted file mode 100644 index f4be10f6894b79a586b725cb463b5333690b5e48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 58699 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 diff --git a/docs/readme/README-UA.md b/docs/readme/README-UA.md new file mode 100644 index 000000000..892e16d19 --- /dev/null +++ b/docs/readme/README-UA.md @@ -0,0 +1,7 @@ +

+ + + +

+ +
\ No newline at end of file From 854f8dbd2f80cf71a16c5a3a3a4db5d38ac65da7 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Fri, 2 Feb 2024 21:59:12 +0800 Subject: [PATCH 09/74] feat: Optimize openim scripts and standard output logs (#1866) * feat: add component check func * fix: fix the outpu error * fix: fix the stderr outpu * fix: fix the component check func * fix: fix the error * fix: fix the output error * fix: del the disruptions code * fix the log output format * fix: fix the tools version * fix: fix the cycle detection * fix: fix the error * fix: fix the flag * fix: add mongo ping detection * fix: fix the tools pkg version * fix: del the err * feat: support openim readme docs Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: support openim readme docs Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: support openim readme docs Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: fix the minio nil error * fix: del the repeated wrap and add err print * fix: fix this bug scripts * Revert "Check" --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> Co-authored-by: luhaoling <2198702716@qq.com> --- cmd/openim-api/main.go | 3 ++- cmd/openim-cmdutils/main.go | 5 +++- cmd/openim-crontask/main.go | 5 +++- cmd/openim-msggateway/main.go | 5 +++- cmd/openim-msgtransfer/main.go | 5 +++- cmd/openim-push/main.go | 5 +++- cmd/openim-rpc/openim-rpc-auth/main.go | 5 +++- .../openim-rpc-conversation/main.go | 5 +++- cmd/openim-rpc/openim-rpc-friend/main.go | 5 +++- cmd/openim-rpc/openim-rpc-group/main.go | 5 +++- cmd/openim-rpc/openim-rpc-msg/main.go | 5 +++- cmd/openim-rpc/openim-rpc-third/main.go | 5 +++- cmd/openim-rpc/openim-rpc-user/main.go | 5 +++- go.mod | 2 +- go.sum | 4 +-- pkg/common/cmd/root.go | 1 - pkg/common/db/unrelation/mongo.go | 5 ++-- .../discoveryregister/zookeeper/zookeeper.go | 2 +- pkg/common/startrpc/start.go | 7 ++--- scripts/check-all.sh | 10 +++++++ scripts/install/openim-api.sh | 10 ++++--- scripts/install/openim-crontask.sh | 7 ++++- scripts/install/openim-msggateway.sh | 5 +++- scripts/install/openim-msgtransfer.sh | 5 +++- scripts/install/openim-push.sh | 7 +++-- scripts/install/openim-rpc.sh | 5 +++- scripts/lib/logging.sh | 4 +++ scripts/lib/util.sh | 27 +++++++++++++------ tools/component/component.go | 26 +++++++++++------- tools/ncpu/ncpu.go | 23 +++++----------- 30 files changed, 146 insertions(+), 67 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 59e0b7f9e..755b35591 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -43,7 +43,8 @@ func main() { apiCmd.AddPortFlag() apiCmd.AddApi(run) if err := apiCmd.Execute(); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-cmdutils/main.go b/cmd/openim-cmdutils/main.go index 058aa2e29..a13bc4f33 100644 --- a/cmd/openim-cmdutils/main.go +++ b/cmd/openim-cmdutils/main.go @@ -15,7 +15,9 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + "os" ) func main() { @@ -54,6 +56,7 @@ func main() { // openIM clear msg --clearAll msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command) if err := msgUtilsCmd.Execute(); err != nil { - panic(err) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go index 3bd0d882b..5061c5408 100644 --- a/cmd/openim-crontask/main.go +++ b/cmd/openim-crontask/main.go @@ -15,13 +15,16 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/tools" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + "os" ) func main() { cronTaskCmd := cmd.NewCronTaskCmd() if err := cronTaskCmd.Exec(tools.StartTask); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go index 6d212e467..25cfe5fa8 100644 --- a/cmd/openim-msggateway/main.go +++ b/cmd/openim-msggateway/main.go @@ -15,7 +15,9 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + "os" ) func main() { @@ -25,6 +27,7 @@ func main() { msgGatewayCmd.AddPrometheusPortFlag() if err := msgGatewayCmd.Exec(); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go index 6895bcecc..e3c2f374f 100644 --- a/cmd/openim-msgtransfer/main.go +++ b/cmd/openim-msgtransfer/main.go @@ -15,7 +15,9 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + "os" ) func main() { @@ -23,6 +25,7 @@ func main() { msgTransferCmd.AddPrometheusPortFlag() msgTransferCmd.AddTransferProgressFlag() if err := msgTransferCmd.Exec(); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go index c19cfda60..79152f945 100644 --- a/cmd/openim-push/main.go +++ b/cmd/openim-push/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/push" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go index 645d8cab8..8ee7053f3 100644 --- a/cmd/openim-rpc/openim-rpc-auth/main.go +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/auth" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go index 13d7db605..259d79abf 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/main.go +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go index ec18306a2..bd85da7b0 100644 --- a/cmd/openim-rpc/openim-rpc-friend/main.go +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/friend" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go index 887329926..e40c04d19 100644 --- a/cmd/openim-rpc/openim-rpc-group/main.go +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/group" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go index dcc3abef5..989746dc7 100644 --- a/cmd/openim-rpc/openim-rpc-msg/main.go +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/msg" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go index cf0bf4b70..fbd386946 100644 --- a/cmd/openim-rpc/openim-rpc-third/main.go +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/third" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go index cbf2a8fc3..160bbe311 100644 --- a/cmd/openim-rpc/openim-rpc-user/main.go +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -15,9 +15,11 @@ package main import ( + "fmt" "github.com/openimsdk/open-im-server/v3/internal/rpc/user" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "os" ) func main() { @@ -28,6 +30,7 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { - panic(err.Error()) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) + os.Exit(-1) } } diff --git a/go.mod b/go.mod index 16a10a945..d82d3390b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.48 - github.com/OpenIMSDK/tools v0.0.32 + github.com/OpenIMSDK/tools v0.0.33 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index 136035cef..fb047dfdc 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.32 h1:b8KwtxXKZTsyyHUcZ4OtSo6s/vVXx4HjMuPxH7Kb7Gg= -github.com/OpenIMSDK/tools v0.0.32/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= +github.com/OpenIMSDK/tools v0.0.33 h1:rvFCxXaXxLv1MJFC4qcoWRGwKBnV+hR68UN2N0/zZhE= +github.com/OpenIMSDK/tools v0.0.33/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 66bec61a7..98ca8f892 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -20,7 +20,6 @@ import ( config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/spf13/cobra" - _ "go.uber.org/automaxprocs" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/log" diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index fe89c6b8a..2bade9a62 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -54,14 +54,15 @@ func NewMongo() (*Mongo, error) { defer cancel() mongoClient, err = mongo.Connect(ctx, options.Client().ApplyURI(uri)) if err == nil { + if err = mongoClient.Ping(ctx, nil); err != nil { + return nil, errs.Wrap(err, uri) + } return &Mongo{db: mongoClient}, nil } if shouldRetry(err) { - fmt.Printf("Failed to connect to MongoDB, retrying: %s\n", err) time.Sleep(time.Second) // exponential backoff could be implemented here continue } - return nil, errs.Wrap(err, uri) } return nil, errs.Wrap(err, uri) } diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index 0082e9833..9c58807c1 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -45,7 +45,7 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er openkeeper.WithLogger(log.NewZkLogger()), ) if err != nil { - uriFormat := "address:%s, username :%s, password :%s, schema:%s." + uriFormat := "address:%s, username:%s, password:%s, schema:%s." errInfo := fmt.Sprintf(uriFormat, config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Username, diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 31fe4fdd5..f7b547834 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -17,7 +17,6 @@ package startrpc import ( "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "log" "net" "net/http" @@ -28,6 +27,8 @@ import ( "syscall" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" "golang.org/x/sync/errgroup" @@ -96,7 +97,7 @@ func Start( err = rpcFn(client, srv) if err != nil { - return errs.Wrap(err) + return err } err = client.Register( rpcRegisterName, @@ -116,7 +117,7 @@ func Start( // Create a HTTP server for prometheus. httpServer := &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)} if err := httpServer.ListenAndServe(); err != nil { - log.Fatal("Unable to start a http server.") + log.Fatal("Unable to start a http server. ", err.Error(), "PrometheusPort:", prometheusPort) } } return nil diff --git a/scripts/check-all.sh b/scripts/check-all.sh index 1f6b740e6..062605ae1 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -49,6 +49,14 @@ print_services_and_ports() { echo "+-------------------------+----------+" } +handle_error() { + echo "An error occurred. Printing ${STDERR_LOG_FILE} contents:" + cat "${STDERR_LOG_FILE}" + exit 1 +} + +trap handle_error ERR + # Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined # Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS @@ -94,3 +102,5 @@ else fi set -e + +trap - ERR \ No newline at end of file diff --git a/scripts/install/openim-api.sh b/scripts/install/openim-api.sh index 2c3c19afb..c81dfcd0d 100755 --- a/scripts/install/openim-api.sh +++ b/scripts/install/openim-api.sh @@ -34,12 +34,15 @@ readonly OPENIM_API_SERVICE_TARGETS=( readonly OPENIM_API_SERVICE_LISTARIES=("${OPENIM_API_SERVICE_TARGETS[@]##*/}") function openim::api::start() { + + rm -rf "$TMP_LOG_FILE" + echo "++ OPENIM_API_SERVICE_LISTARIES: ${OPENIM_API_SERVICE_LISTARIES[@]}" echo "++ OPENIM_API_PORT_LISTARIES: ${OPENIM_API_PORT_LISTARIES[@]}" echo "++ OpenIM API config path: ${OPENIM_API_CONFIG}" - + openim::log::info "Starting ${SERVER_NAME} ..." - + printf "+------------------------+--------------+\n" printf "| Service Name | Port |\n" printf "+------------------------+--------------+\n" @@ -80,8 +83,7 @@ function openim::api::start_service() { local prometheus_port="$3" local cmd="${OPENIM_OUTPUT_HOSTBIN}/${binary_name} --port ${service_port} -c ${OPENIM_API_CONFIG}" - - nohup ${cmd} >> "${LOG_FILE}" 2>&1 & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & if [ $? -ne 0 ]; then openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}." diff --git a/scripts/install/openim-crontask.sh b/scripts/install/openim-crontask.sh index cc9e686ff..6068e97d5 100755 --- a/scripts/install/openim-crontask.sh +++ b/scripts/install/openim-crontask.sh @@ -44,14 +44,19 @@ OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) SERVER_NAME="openim-crontask" function openim::crontask::start() { + + rm -rf "$TMP_LOG_FILE" + openim::log::info "Start OpenIM Cron, binary root: ${SERVER_NAME}" openim::log::status "Start OpenIM Cron, path: ${OPENIM_CRONTASK_BINARY}" openim::util::stop_services_with_name ${OPENIM_CRONTASK_BINARY} openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}" - nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2>&1 & + + nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & openim::util::check_process_names ${SERVER_NAME} + } ###################################### Linux Systemd ###################################### diff --git a/scripts/install/openim-msggateway.sh b/scripts/install/openim-msggateway.sh index d9fec4928..4e591deca 100755 --- a/scripts/install/openim-msggateway.sh +++ b/scripts/install/openim-msggateway.sh @@ -26,6 +26,9 @@ openim::util::set_max_fd 200000 SERVER_NAME="openim-msggateway" function openim::msggateway::start() { + + rm -rf "$TMP_LOG_FILE" + openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGGATEWAY_BINARY}" @@ -61,7 +64,7 @@ function openim::msggateway::start() { PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" fi - nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2>&1 & + nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & done openim::util::check_process_names ${SERVER_NAME} diff --git a/scripts/install/openim-msgtransfer.sh b/scripts/install/openim-msgtransfer.sh index 1cead3a9a..def22c38b 100755 --- a/scripts/install/openim-msgtransfer.sh +++ b/scripts/install/openim-msgtransfer.sh @@ -28,6 +28,9 @@ openim::util::set_max_fd 200000 SERVER_NAME="openim-msgtransfer" function openim::msgtransfer::start() { + + rm -rf "$TMP_LOG_FILE" + openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGTRANSFER_BINARY}" @@ -56,7 +59,7 @@ function openim::msgtransfer::start() { if [[ -n "${OPENIM_PROMETHEUS_PORTS[$i]}" ]]; then PROMETHEUS_PORT_OPTION="--prometheus_port ${OPENIM_PROMETHEUS_PORTS[$i]}" fi - nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i}>> ${LOG_FILE} 2>&1 & + nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & done openim::util::check_process_names "${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}" diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh index d43743e4f..4d14ca675 100755 --- a/scripts/install/openim-push.sh +++ b/scripts/install/openim-push.sh @@ -50,6 +50,9 @@ OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) SERVER_NAME="openim-push" function openim::push::start() { + + rm -rf "$TMP_LOG_FILE" + openim::log::status "Start OpenIM Push, binary root: ${SERVER_NAME}" openim::log::info "Start OpenIM Push, path: ${OPENIM_PUSH_BINARY}" @@ -70,9 +73,9 @@ function openim::push::start() { for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" - nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2>&1 & + nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & done - + openim::util::check_process_names ${SERVER_NAME} } diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh index 966eef928..00031f211 100755 --- a/scripts/install/openim-rpc.sh +++ b/scripts/install/openim-rpc.sh @@ -102,6 +102,8 @@ readonly OPENIM_RPC_PROM_PORT_TARGETS readonly OPENIM_RPC_PROM_PORT_LISTARIES=("${OPENIM_RPC_PROM_PORT_TARGETS[@]##*/}") function openim::rpc::start() { + rm -rf "$TMP_LOG_FILE" + echo "OPENIM_RPC_SERVICE_LISTARIES: ${OPENIM_RPC_SERVICE_LISTARIES[@]}" echo "OPENIM_RPC_PROM_PORT_LISTARIES: ${OPENIM_RPC_PROM_PORT_LISTARIES[@]}" echo "OPENIM_RPC_PORT_LISTARIES: ${OPENIM_RPC_PORT_LISTARIES[@]}" @@ -123,6 +125,7 @@ function openim::rpc::start() { for ((i = 0; i < ${#OPENIM_RPC_SERVICE_LISTARIES[*]}; i++)); do # openim::util::stop_services_with_name ${OPENIM_RPC_SERVICE_LISTARIES openim::util::stop_services_on_ports ${OPENIM_RPC_PORT_LISTARIES[$i]} + openim::util::stop_services_on_ports ${OPENIM_RPC_PROM_PORT_LISTARIES[$i]} openim::log::info "OpenIM ${OPENIM_RPC_SERVICE_LISTARIES[$i]} config path: ${OPENIM_RPC_CONFIG}" @@ -157,7 +160,7 @@ function openim::rpc::start_service() { printf "Specifying prometheus port: %s\n" "${prometheus_port}" cmd="${cmd} --prometheus_port ${prometheus_port}" fi - nohup ${cmd} >> "${LOG_FILE}" 2>&1 & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & } ###################################### Linux Systemd ###################################### diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index c520850e8..2fa77dd83 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -26,10 +26,14 @@ fi # Set the log file path LOG_FILE="${OPENIM_OUTPUT}/logs/openim_$(date '+%Y%m%d').log" +STDERR_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_error_$(date '+%Y%m%d').log" +TMP_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_tmp_$(date '+%Y%m%d').log" if [[ ! -d "${OPENIM_OUTPUT}/logs" ]]; then mkdir -p "${OPENIM_OUTPUT}/logs" touch "$LOG_FILE" + touch "$STDERR_LOG_FILE" + touch "$TMP_LOG_FILE" fi # Define the logging function diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index a40668d70..b93f45205 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -360,7 +360,9 @@ openim::util::check_ports() { # If any of the processes is not running, return a status of 1. if [[ ${#not_started[@]} -ne 0 ]]; then - echo "++++ OpenIM Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else openim::log::success "All specified processes are running." @@ -444,9 +446,12 @@ openim::util::check_process_names() { # Return status if [[ ${#not_started[@]} -ne 0 ]]; then - echo "++++ OpenIM Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else + echo "" openim::log::success "All processes are running." return 0 fi @@ -1536,12 +1541,12 @@ openim::util::check_ports() { if [[ "$OSTYPE" == "linux-gnu"* ]]; then if command -v ss > /dev/null 2>&1; then info=$(ss -ltnp | grep ":$port" || true) - echo "!!!!!!!!!!! port=$port" - echo "!!!!!!!!!!! info=$info" + openim::color::echo $COLOR_RED "!!!!!!!! port=$port" + openim::color::echo $COLOR_RED "!!!!!!!! info=$info" else info=$(netstat -ltnp | grep ":$port" || true) - echo "!!!!!!!!!!! port=$port" - echo "!!!!!!!!!!! info=$info" + openim::color::echo $COLOR_RED "!!!!!!!! port=$port" + openim::color::echo $COLOR_RED "!!!!!!!! info=$info" fi elif [[ "$OSTYPE" == "darwin"* ]]; then # For macOS, use lsof @@ -1594,7 +1599,10 @@ openim::util::check_ports() { # If any of the processes is not running, return a status of 1. if [[ ${#not_started[@]} -ne 0 ]]; then - echo "++++ OpenIM Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + echo "" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else openim::log::success "All specified processes are running." @@ -1678,9 +1686,12 @@ openim::util::check_process_names() { # Return status if [[ ${#not_started[@]} -ne 0 ]]; then - echo "++++ OpenIM Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else + echo "" openim::log::success "All processes are running." return 0 fi diff --git a/tools/component/component.go b/tools/component/component.go index 787ca8af6..bd6007015 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -15,6 +15,7 @@ package main import ( + "errors" "flag" "fmt" "github.com/IBM/sarama" @@ -56,6 +57,7 @@ func initCfg() error { type checkFunc struct { name string function func() error + flag bool } func main() { @@ -86,13 +88,17 @@ func main() { var err error allSuccess := true - for _, check := range checks { - err = check.function() - if err != nil { - component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err)) - allSuccess = false - } else { - component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name)) + for index, check := range checks { + if !check.flag { + err = check.function() + if err != nil { + component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err)) + allSuccess = false + + } else { + checks[index].flag = true + component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name)) + } } } @@ -120,7 +126,7 @@ func checkMinio() error { // Check if MinIO is enabled if config.Config.Object.Enable != "minio" { - return nil + return errs.Wrap(errors.New("minio.Enable is empty")) } minio := &component.Minio{ ApiURL: config.Config.Object.ApiURL, @@ -130,7 +136,7 @@ func checkMinio() error { SignEndpoint: config.Config.Object.Minio.SignEndpoint, UseSSL: getEnv("MINIO_USE_SSL", "false"), } - _, err := component.CheckMinio(minio) + err := component.CheckMinio(minio) return err } @@ -149,7 +155,7 @@ func checkKafka() error { Addr: config.Config.Kafka.Addr, } - _, kafkaClient, err := component.CheckKafka(kafkaStu) + kafkaClient, err := component.CheckKafka(kafkaStu) if err != nil { return err } diff --git a/tools/ncpu/ncpu.go b/tools/ncpu/ncpu.go index 7ca3dff5e..ca2409a6f 100644 --- a/tools/ncpu/ncpu.go +++ b/tools/ncpu/ncpu.go @@ -1,17 +1,3 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - package main import ( @@ -22,6 +8,11 @@ import ( ) func main() { - maxprocs.Set() - fmt.Print(runtime.GOMAXPROCS(0)) + // Set maxprocs with a custom logger that does nothing to ignore logs. + maxprocs.Set(maxprocs.Logger(func(string, ...interface{}) { + // Intentionally left blank to suppress all log output from automaxprocs. + })) + + // Now this will print the GOMAXPROCS value without printing the automaxprocs log message. + fmt.Println(runtime.GOMAXPROCS(0)) } From 8729f90d0293347763b57961b9aeb8a1f14e855f Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Sun, 4 Feb 2024 10:40:26 +0800 Subject: [PATCH 10/74] cicd: bump League Patch (#1874) --- cmd/openim-api/main.go | 4 +++- cmd/openim-cmdutils/main.go | 3 ++- cmd/openim-crontask/main.go | 3 ++- cmd/openim-msggateway/main.go | 3 ++- cmd/openim-msgtransfer/main.go | 3 ++- cmd/openim-push/main.go | 3 ++- cmd/openim-rpc/openim-rpc-auth/main.go | 3 ++- cmd/openim-rpc/openim-rpc-conversation/main.go | 3 ++- cmd/openim-rpc/openim-rpc-friend/main.go | 3 ++- cmd/openim-rpc/openim-rpc-group/main.go | 3 ++- cmd/openim-rpc/openim-rpc-msg/main.go | 3 ++- cmd/openim-rpc/openim-rpc-third/main.go | 3 ++- cmd/openim-rpc/openim-rpc-user/main.go | 3 ++- go.mod | 1 - go.sum | 3 --- internal/msgtransfer/init.go | 3 ++- internal/tools/cron_task.go | 3 ++- pkg/common/db/mgo/conversation.go | 3 ++- pkg/common/db/mgo/group.go | 3 ++- pkg/common/db/mgo/group_member.go | 1 + pkg/common/db/mgo/group_request.go | 1 + pkg/common/db/unrelation/mongo.go | 1 + .../discoveryregister/zookeeper/zookeeper.go | 3 ++- pkg/common/kafka/consumer_group.go | 3 ++- pkg/common/kafka/producer.go | 3 ++- tools/component/component.go | 8 +++++--- tools/ncpu/ncpu.go | 14 ++++++++++++++ 27 files changed, 63 insertions(+), 27 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 755b35591..a45bcbdd8 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -17,7 +17,6 @@ package main import ( "context" "fmt" - "github.com/OpenIMSDK/tools/errs" "net" "net/http" _ "net/http/pprof" @@ -27,8 +26,11 @@ import ( "syscall" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/cmd/openim-cmdutils/main.go b/cmd/openim-cmdutils/main.go index a13bc4f33..45b324766 100644 --- a/cmd/openim-cmdutils/main.go +++ b/cmd/openim-cmdutils/main.go @@ -16,8 +16,9 @@ package main import ( "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "os" + + "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" ) func main() { diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go index 5061c5408..324001690 100644 --- a/cmd/openim-crontask/main.go +++ b/cmd/openim-crontask/main.go @@ -16,9 +16,10 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/tools" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "os" ) func main() { diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go index 25cfe5fa8..5339891c8 100644 --- a/cmd/openim-msggateway/main.go +++ b/cmd/openim-msggateway/main.go @@ -16,8 +16,9 @@ package main import ( "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "os" + + "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" ) func main() { diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go index e3c2f374f..cf1b44a55 100644 --- a/cmd/openim-msgtransfer/main.go +++ b/cmd/openim-msgtransfer/main.go @@ -16,8 +16,9 @@ package main import ( "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "os" + + "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" ) func main() { diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go index 79152f945..77f75cb4e 100644 --- a/cmd/openim-push/main.go +++ b/cmd/openim-push/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/push" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go index 8ee7053f3..b29efd484 100644 --- a/cmd/openim-rpc/openim-rpc-auth/main.go +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/auth" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go index 259d79abf..f9ac8cd27 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/main.go +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go index bd85da7b0..82d71d522 100644 --- a/cmd/openim-rpc/openim-rpc-friend/main.go +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/friend" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go index e40c04d19..360042f84 100644 --- a/cmd/openim-rpc/openim-rpc-group/main.go +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/group" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go index 989746dc7..bed57f522 100644 --- a/cmd/openim-rpc/openim-rpc-msg/main.go +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/msg" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go index fbd386946..4868ce149 100644 --- a/cmd/openim-rpc/openim-rpc-third/main.go +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/third" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go index 160bbe311..a77a2f768 100644 --- a/cmd/openim-rpc/openim-rpc-user/main.go +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -16,10 +16,11 @@ package main import ( "fmt" + "os" + "github.com/openimsdk/open-im-server/v3/internal/rpc/user" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "os" ) func main() { diff --git a/go.mod b/go.mod index d82d3390b..3da7c3ecd 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,6 @@ require ( github.com/spf13/pflag v1.0.5 github.com/stathat/consistent v1.0.0 github.com/tencentyun/cos-go-sdk-v5 v0.7.45 - go.uber.org/automaxprocs v1.5.3 golang.org/x/sync v0.4.0 gopkg.in/src-d/go-git.v4 v4.13.1 gotest.tools v2.2.0+incompatible diff --git a/go.sum b/go.sum index fb047dfdc..84620fe7d 100644 --- a/go.sum +++ b/go.sum @@ -277,7 +277,6 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -352,8 +351,6 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= -go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 969761a9d..83ec00749 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,11 +18,12 @@ import ( "context" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "log" "net/http" "sync" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/mw" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index cf2068d8e..40e1c0a87 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -17,12 +17,13 @@ package tools import ( "context" "fmt" - "github.com/OpenIMSDK/tools/errs" "os" "os/signal" "syscall" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/redis/go-redis/v9" "github.com/robfig/cron/v3" diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index 0b8d597dc..d0a46ae47 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -16,9 +16,10 @@ package mgo import ( "context" - "github.com/OpenIMSDK/tools/errs" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go index 9a4c660a6..922bfd424 100644 --- a/pkg/common/db/mgo/group.go +++ b/pkg/common/db/mgo/group.go @@ -16,9 +16,10 @@ package mgo import ( "context" - "github.com/OpenIMSDK/tools/errs" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/db/mgo/group_member.go b/pkg/common/db/mgo/group_member.go index fce79830e..e28432b11 100644 --- a/pkg/common/db/mgo/group_member.go +++ b/pkg/common/db/mgo/group_member.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" diff --git a/pkg/common/db/mgo/group_request.go b/pkg/common/db/mgo/group_request.go index dce0878ee..d20682239 100644 --- a/pkg/common/db/mgo/group_request.go +++ b/pkg/common/db/mgo/group_request.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 2bade9a62..4c093b3c3 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -27,6 +27,7 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw/specialerror" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index 9c58807c1..6e55b6b8b 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -16,11 +16,12 @@ package zookeeper import ( "fmt" - "github.com/OpenIMSDK/tools/errs" "os" "strings" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/discoveryregistry" openkeeper "github.com/OpenIMSDK/tools/discoveryregistry/zookeeper" "github.com/OpenIMSDK/tools/log" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 87e6d5686..6e6f83fca 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -16,9 +16,10 @@ package kafka import ( "context" - "github.com/OpenIMSDK/tools/errs" "strings" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index b9f0b4656..417aadb54 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -18,10 +18,11 @@ import ( "bytes" "context" "errors" - "github.com/OpenIMSDK/tools/errs" "strings" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/log" diff --git a/tools/component/component.go b/tools/component/component.go index bd6007015..6b879d7f8 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -18,14 +18,16 @@ import ( "errors" "flag" "fmt" + "os" + "strings" + "time" + "github.com/IBM/sarama" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" - "os" - "strings" - "time" "github.com/OpenIMSDK/tools/component" "github.com/OpenIMSDK/tools/errs" diff --git a/tools/ncpu/ncpu.go b/tools/ncpu/ncpu.go index ca2409a6f..062618b27 100644 --- a/tools/ncpu/ncpu.go +++ b/tools/ncpu/ncpu.go @@ -1,3 +1,17 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( From 9610da9123fea0fb3954553be7ebd138a7beb8d7 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Sun, 4 Feb 2024 20:05:53 +0800 Subject: [PATCH 11/74] fix(main): fix openim scripts start rpc log (#1877) * Update start.go * Update start.go --- pkg/common/startrpc/start.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index f7b547834..f6cda2ffb 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -17,7 +17,6 @@ package startrpc import ( "errors" "fmt" - "log" "net" "net/http" "os" @@ -117,7 +116,8 @@ func Start( // Create a HTTP server for prometheus. httpServer := &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)} if err := httpServer.ListenAndServe(); err != nil { - log.Fatal("Unable to start a http server. ", err.Error(), "PrometheusPort:", prometheusPort) + fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v PrometheusPort: %d \n\n", err, prometheusPort) + os.Exit(-1) } } return nil From 311d42283b2a0a022f8827fb0927ca30601fbb73 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Sun, 4 Feb 2024 20:06:34 +0800 Subject: [PATCH 12/74] feat: fix openim logs and ci (#1878) --- scripts/lib/logging.sh | 14 +++++++++++--- scripts/lib/util.sh | 4 ---- scripts/start-all.sh | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index 2fa77dd83..8f2bb33cf 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -131,16 +131,24 @@ openim::log::error_exit() { exit "${code}" } -# Log an error but keep going. Don't dump the stack or exit. +# Log an error but keep going. Don't dump the stack or exit. openim::log::error() { + # Define red color + red='\033[0;31m' + # No color (reset) + nc='\033[0m' # No Color + timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]") - echo_log "!!! ${timestamp} ${1-}" >&2 + # Apply red color for error message + echo_log "${red}!!! ${timestamp} ${1-}${nc}" >&2 shift for message; do - echo_log " ${message}" >&2 + # Apply red color for subsequent lines of the error message + echo_log "${red} ${message}${nc}" >&2 done } + # Print an usage message to stderr. The arguments are printed directly. openim::log::usage() { echo_log >&2 diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index b93f45205..eaefaf22a 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -1541,12 +1541,8 @@ openim::util::check_ports() { if [[ "$OSTYPE" == "linux-gnu"* ]]; then if command -v ss > /dev/null 2>&1; then info=$(ss -ltnp | grep ":$port" || true) - openim::color::echo $COLOR_RED "!!!!!!!! port=$port" - openim::color::echo $COLOR_RED "!!!!!!!! info=$info" else info=$(netstat -ltnp | grep ":$port" || true) - openim::color::echo $COLOR_RED "!!!!!!!! port=$port" - openim::color::echo $COLOR_RED "!!!!!!!! info=$info" fi elif [[ "$OSTYPE" == "darwin"* ]]; then # For macOS, use lsof diff --git a/scripts/start-all.sh b/scripts/start-all.sh index 5f34cbdbe..ca03f0c3c 100755 --- a/scripts/start-all.sh +++ b/scripts/start-all.sh @@ -82,4 +82,4 @@ execute_scripts openim::log::info "\n## Post Starting OpenIM services" ${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start -openim::log::success "✨ All OpenIM services have been successfully started!" \ No newline at end of file +openim::color::echo $COLOR_BLUE "✨ All OpenIM services have been successfully started!" \ No newline at end of file From ee245157614af085525b88888daae0c51826276b Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:13:17 +0800 Subject: [PATCH 13/74] feat: add getUserToken api and add ex field in getSortedConversationListResp (#1880) * fix: del the manager config and manger init statement * fix: fix the Manger judge condition * fix: fix revokeMsg error * fix: find erors * fix: find error * fix: fix the AdminAccount error * fix: del the debug statement * fix: fix the component check func * fix: fix the get zkAddress error * fix: fix the kafka client close error * fix: add env in minio connected * fix: del the minio env * fix: fix the go.mod tools version * fix: del get env in minio conneted * feat: add GetUserToken api and add ex field in GetSortedConversationList resp * fix: fix the go.mod version * fix: add lack method * fix: add a method * fix: add lack implement * fix: fix the tools pkg version * fix: del the unuser pkg * fix: add Limiting judgement of get admin token --- go.mod | 4 +--- go.sum | 4 ++-- internal/api/auth.go | 4 ++++ internal/api/route.go | 1 + internal/rpc/auth/auth.go | 22 ++++++++++++++++++++++ internal/rpc/conversation/conversaion.go | 5 +++++ internal/rpc/group/group.go | 5 +++++ internal/rpc/user/user.go | 5 +++++ pkg/authverify/token.go | 3 +-- 9 files changed, 46 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 3da7c3ecd..ab138e68c 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible - github.com/OpenIMSDK/protocol v0.0.48 + github.com/OpenIMSDK/protocol v0.0.55 github.com/OpenIMSDK/tools v0.0.33 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 @@ -155,5 +155,3 @@ require ( golang.org/x/crypto v0.17.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) - -replace github.com/OpenIMSDK/protocol v0.0.47 => github.com/AndrewZuo01/protocol v0.0.0-20240112093520-fd9c53e27b94 diff --git a/go.sum b/go.sum index 84620fe7d..94a516366 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIw github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= -github.com/OpenIMSDK/protocol v0.0.48 h1:8MIMjyzJRsruYhVv2ZKArFiOveroaofDOb3dlAdgjsw= -github.com/OpenIMSDK/protocol v0.0.48/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= +github.com/OpenIMSDK/protocol v0.0.55 h1:eBjg8DyuhxGmuCUjpoZjg6MJJJXU/xJ3xJwFhrn34yA= +github.com/OpenIMSDK/protocol v0.0.55/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= github.com/OpenIMSDK/tools v0.0.33 h1:rvFCxXaXxLv1MJFC4qcoWRGwKBnV+hR68UN2N0/zZhE= github.com/OpenIMSDK/tools v0.0.33/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= diff --git a/internal/api/auth.go b/internal/api/auth.go index 44a97a013..88539f63a 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -33,6 +33,10 @@ func (o *AuthApi) UserToken(c *gin.Context) { a2r.Call(auth.AuthClient.UserToken, o.Client, c) } +func (o *AuthApi) GetUserToken(c *gin.Context) { + a2r.Call(auth.AuthClient.GetUserToken, o.Client, c) +} + func (o *AuthApi) ParseToken(c *gin.Context) { a2r.Call(auth.AuthClient.ParseToken, o.Client, c) } diff --git a/internal/api/route.go b/internal/api/route.go index 10907d086..24ed5f6bb 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -150,6 +150,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive { a := NewAuthApi(*authRpc) authRouterGroup.POST("/user_token", a.UserToken) + authRouterGroup.POST("/get_user_token", ParseToken, a.GetUserToken) authRouterGroup.POST("/parse_token", a.ParseToken) authRouterGroup.POST("/force_logout", ParseToken, a.ForceLogout) } diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index ee8ead194..eaf63f868 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -80,6 +80,28 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (* return &resp, nil } +func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenReq) (*pbauth.GetUserTokenResp, error) { + if err := authverify.CheckAdmin(ctx); err != nil { + return nil, err + } + resp := pbauth.GetUserTokenResp{} + + if authverify.IsManagerUserID(req.UserID) { + return nil, errs.ErrNoPermission.Wrap("don't get Admin token") + } + + if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil { + return nil, err + } + token, err := s.authDatabase.CreateToken(ctx, req.UserID, int(req.PlatformID)) + if err != nil { + return nil, err + } + resp.Token = token + resp.ExpireTimeSeconds = config.Config.TokenPolicy.Expire * 24 * 60 * 60 + return &resp, nil +} + func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) { claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret()) if err != nil { diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 40803089c..3317359e5 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -51,6 +51,11 @@ type conversationServer struct { conversationNotificationSender *notification.ConversationNotificationSender } +func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) { + //TODO implement me + panic("implement me") +} + func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis() if err != nil { diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index abc271651..1d068b1b2 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -108,6 +108,11 @@ type groupServer struct { msgRpcClient rpcclient.MessageRpcClient } +func (s *groupServer) GetJoinedGroupIDs(ctx context.Context, req *pbgroup.GetJoinedGroupIDsReq) (*pbgroup.GetJoinedGroupIDsResp, error) { + //TODO implement me + panic("implement me") +} + func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { defer log.ZDebug(ctx, "NotificationUserInfoUpdate return") members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID) diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index e5567f436..6f9e2949f 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -61,6 +61,11 @@ type userServer struct { RegisterCenter registry.SvcDiscoveryRegistry } +func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGroupOnlineUserReq) (*pbuser.GetGroupOnlineUserResp, error) { + //TODO implement me + panic("implement me") +} + func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis() if err != nil { diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index 97bb03391..b951bf219 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -48,8 +48,7 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { } func IsAppManagerUid(ctx context.Context) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || - utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) + return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) } func CheckAdmin(ctx context.Context) error { From 0865eb65b1eaed0b5a699c8571255b3fd6fb09a3 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Mon, 5 Feb 2024 10:08:55 +0800 Subject: [PATCH 14/74] fix: kill 10 process optimization (#1883) --- scripts/lib/util.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index eaefaf22a..cace53645 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -486,7 +486,7 @@ openim::util::stop_services_on_ports() { local pid=$(echo $line | awk '{print $2}') # Try to stop the service by killing its process. - if kill -TERM $pid; then + if kill -10 $pid; then stopped+=($port) else not_stopped+=($port) @@ -563,7 +563,7 @@ openim::util::stop_services_with_name() { # If there's a Process ID, it means the service with the name is running. if [[ -n $pid ]]; then # Try to stop the service by killing its process. - if kill -TERM $pid 2>/dev/null; then + if kill -10 $pid 2>/dev/null; then stopped_this_time=true fi fi @@ -1722,7 +1722,7 @@ openim::util::stop_services_on_ports() { local pid=$(echo $line | awk '{print $2}') # Try to stop the service by killing its process. - if kill -TERM $pid; then + if kill -10 $pid; then stopped+=($port) else not_stopped+=($port) @@ -1799,7 +1799,7 @@ openim::util::stop_services_with_name() { # If there's a Process ID, it means the service with the name is running. if [[ -n $pid ]]; then # Try to stop the service by killing its process. - if kill -TERM $pid 2>/dev/null; then + if kill -10 $pid 2>/dev/null; then stopped_this_time=true fi fi From 31381935f188210bc905d8cbe5c0959b8584fb61 Mon Sep 17 00:00:00 2001 From: "fengyun.rui" Date: Mon, 5 Feb 2024 10:37:53 +0800 Subject: [PATCH 15/74] fix: graceful exit for kafka consumer of msgtransfer (#1483) * fix: graceful exit for kafka consumer of msgtransfer Signed-off-by: rfyiamcool * Update init.go * Update init.go --------- Signed-off-by: rfyiamcool Co-authored-by: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> --- internal/msgtransfer/init.go | 74 +++++++++++++++---- .../msgtransfer/online_history_msg_handler.go | 58 +++++++++++---- pkg/common/kafka/consumer_group.go | 27 +++++-- 3 files changed, 124 insertions(+), 35 deletions(-) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 83ec00749..65a6b1935 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,7 +18,10 @@ import ( "context" "errors" "fmt" - "log" + "os" + "os/signal" + "syscall" + "time" "net/http" "sync" @@ -30,7 +33,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - + "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -51,11 +54,13 @@ func StartTransfer(prometheusPort int) error { if err != nil { return err } + mongo, err := unrelation.NewMongo() if err != nil { return err } - if err := mongo.CreateMsgIndex(); err != nil { + + if err = mongo.CreateMsgIndex(); err != nil { return err } client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) @@ -66,6 +71,7 @@ func StartTransfer(prometheusPort int) error { if err != nil { return err } + if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { return err } @@ -103,26 +109,62 @@ func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcCli func (m *MsgTransfer) Start(prometheusPort int) error { ctx := context.Background() - var wg sync.WaitGroup - wg.Add(1) fmt.Println("start msg transfer", "prometheusPort:", prometheusPort) if prometheusPort <= 0 { return errs.Wrap(errors.New("prometheusPort not correct")) } - go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyCH) - go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyMongoCH) + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + + m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyCH) + }() + + wg.Add(1) + go func() { + defer wg.Done() + + m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyMongoCH) + }() if config.Config.Prometheus.Enable { - reg := prometheus.NewRegistry() - reg.MustRegister( - collectors.NewGoCollector(), - ) - reg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...) - http.Handle("/metrics", promhttp.HandlerFor(reg, promhttp.HandlerOpts{Registry: reg})) - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil)) + go func() { + proreg := prometheus.NewRegistry() + proreg.MustRegister( + collectors.NewGoCollector(), + ) + proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...) + http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg})) + err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil) + if err != nil && err != http.ErrServerClosed { + panic(err) + } + }() } - //////////////////////////////////////// - wg.Wait() + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + <-sigs + + // graceful close kafka client. + go m.historyCH.historyConsumerGroup.Close() + go m.historyMongoCH.historyConsumerGroup.Close() + + done := make(chan struct{}, 1) + go func() { + wg.Wait() + close(done) + }() + + select { + case <-done: + log.ZInfo(context.Background(), "msgtrasfer exit successfully") + case <-time.After(15 * time.Second): + log.ZError(context.Background(), "msgtransfer force to exit, timeout 15s", nil) + } + return nil } diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 35af330c9..6678715d4 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -19,6 +19,7 @@ import ( "strconv" "strings" "sync" + "sync/atomic" "time" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" @@ -431,16 +432,29 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim( log.ZDebug(context.Background(), "online new session msg come", "highWaterMarkOffset", claim.HighWaterMarkOffset(), "topic", claim.Topic(), "partition", claim.Partition()) - split := 1000 - rwLock := new(sync.RWMutex) - messages := make([]*sarama.ConsumerMessage, 0, 1000) - ticker := time.NewTicker(time.Millisecond * 100) + var ( + split = 1000 + rwLock = new(sync.RWMutex) + messages = make([]*sarama.ConsumerMessage, 0, 1000) + ticker = time.NewTicker(time.Millisecond * 100) + wg = sync.WaitGroup{} + running = new(atomic.Bool) + ) + + wg.Add(1) go func() { + defer wg.Done() + for { select { case <-ticker.C: + // if the buffer is empty and running is false, return loop. if len(messages) == 0 { + if !running.Load() { + return + } + continue } @@ -473,17 +487,35 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim( } }() - for msg := range claim.Messages() { - if len(msg.Value) == 0 { - continue - } + wg.Add(1) + go func() { + defer wg.Done() - rwLock.Lock() - messages = append(messages, msg) - rwLock.Unlock() + for running.Load() { + select { + case msg, ok := <-claim.Messages(): + if !ok { + running.Store(false) + return + } - sess.MarkMessage(msg, "") - } + if len(msg.Value) == 0 { + continue + } + + rwLock.Lock() + messages = append(messages, msg) + rwLock.Unlock() + + sess.MarkMessage(msg, "") + + case <-sess.Context().Done(): + running.Store(false) + return + } + } + }() + wg.Wait() return nil } diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 6e6f83fca..3f444cc1f 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -16,18 +16,18 @@ package kafka import ( "context" + "errors" + "github.com/IBM/sarama" "strings" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/IBM/sarama" ) type MConsumerGroup struct { + ctx context.Context + cancel context.CancelFunc + sarama.ConsumerGroup groupID string topics []string @@ -54,7 +54,10 @@ func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []str if err != nil { return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, config.Config.Kafka.Username, config.Config.Kafka.Password) } + + ctx, cancel := context.WithCancel(context.Background()) return &MConsumerGroup{ + ctx, cancel, consumerGroup, groupID, topics, @@ -68,7 +71,14 @@ func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) contex func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) { log.ZDebug(context.Background(), "register consumer group", "groupID", mc.groupID) for { - err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler) + err := mc.ConsumerGroup.Consume(mc.ctx, mc.topics, handler) + if errors.Is(err, sarama.ErrClosedConsumerGroup) { + return + } + if mc.ctx.Err() != nil { + return + } + if err != nil { log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID) } @@ -77,3 +87,8 @@ func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler } } } + +func (mc *MConsumerGroup) Close() { + mc.cancel() + mc.ConsumerGroup.Close() +} From 6c7b94f03f271451e7e7348ab998928deb17861a Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:45:45 +0800 Subject: [PATCH 16/74] =?UTF-8?q?[Auto=20PR=20=F0=9F=A4=96]=20Bump=20Leagu?= =?UTF-8?q?e=20Patch=20auto=20PR=20(#1884)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cicd: bump League Patch * Update project-progress.yml --------- Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- .github/workflows/project-progress.yml | 5 ++++- internal/rpc/conversation/conversaion.go | 5 ++++- pkg/authverify/token.go | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/project-progress.yml b/.github/workflows/project-progress.yml index 3ec8b7b1f..0b071b3a8 100644 --- a/.github/workflows/project-progress.yml +++ b/.github/workflows/project-progress.yml @@ -24,6 +24,9 @@ on: pull_request: types: - assigned + branches-ignore: + - 'asf-auto-updates' + - 'ignore' jobs: move-assigned-card: @@ -33,4 +36,4 @@ jobs: with: project: openim-powerful column: In Progress - repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} \ No newline at end of file + repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 3317359e5..8558a23ea 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -51,7 +51,10 @@ type conversationServer struct { conversationNotificationSender *notification.ConversationNotificationSender } -func (c *conversationServer) GetConversationNotReceiveMessageUserIDs(ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) { +func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( + ctx context.Context, + req *pbconversation.GetConversationNotReceiveMessageUserIDsReq, +) (*pbconversation.GetConversationNotReceiveMessageUserIDsResp, error) { //TODO implement me panic("implement me") } diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index b951bf219..97bb03391 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -48,7 +48,8 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { } func IsAppManagerUid(ctx context.Context) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) + return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || + utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) } func CheckAdmin(ctx context.Context) error { From 7862fa50038e4c4737f1ca50a0aa9b77da3b842f Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Tue, 6 Feb 2024 14:11:27 +0800 Subject: [PATCH 17/74] docs: add README.md JP (#1891) * docs: README JP Signed-off-by: wxuanF <2569456943@qq.com> * Update openimci.yml * Update golangci-lint.yml * docs: README JP Signed-off-by: wxuanF <2569456943@qq.com> * docs: README JP Signed-off-by: wxuanF <2569456943@qq.com> * docs: README JP Signed-off-by: wxuanF <2569456943@qq.com> --------- Signed-off-by: wxuanF <2569456943@qq.com> Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- .github/workflows/golangci-lint.yml | 4 +- .github/workflows/openimci.yml | 3 - docs/readme/README-JP.md | 190 ++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 4 deletions(-) create mode 100644 docs/readme/README-JP.md diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 67dc5a6b8..250cb96cb 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -28,6 +28,8 @@ jobs: with: go-version: '1.21' cache: false + - name: OpenIM Scripts Verification(make verify) + run: sudo make verify - name: golangci-lint uses: golangci/golangci-lint-action@v3.7.0 with: @@ -47,4 +49,4 @@ jobs: only-new-issues: true # Optional:The mode to install golangci-lint. It can be 'binary' or 'goinstall'. - # install-mode: "goinstall" \ No newline at end of file + # install-mode: "goinstall" diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml index 8aa38d941..2d42e3155 100644 --- a/.github/workflows/openimci.yml +++ b/.github/workflows/openimci.yml @@ -70,9 +70,6 @@ jobs: version: '3.x' # If available, use the latest major version that's compatible repo-token: ${{ secrets.GITHUB_TOKEN }} - - name: OpenIM Scripts Verification(make verify) - run: sudo make verify - - name: Module Operations run: | sudo make tidy diff --git a/docs/readme/README-JP.md b/docs/readme/README-JP.md new file mode 100644 index 000000000..c1b0eabf5 --- /dev/null +++ b/docs/readme/README-JP.md @@ -0,0 +1,190 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ + +## Ⓜ️ OpenIMについて + +OpenIMは、アプリケーション内でチャット、音声通話、通知、AIチャットボットなどの通信機能を統合するために特別に設計されたサービスプラットフォームです。一連の強力なAPIとWebhooksを提供することで、開発者はアプリケーションに簡単にこれらの通信機能を統合できます。OpenIM自体は独立したチャットアプリではなく、アプリケーションにサポートを提供し、豊富な通信機能を実現するプラットフォームです。以下の図は、AppServer、AppClient、OpenIMServer、OpenIMSDK間の相互作用を示しています。 + + + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 OpenIMSDKについて + + **OpenIMSDK**は、**OpenIMServer**用に設計されたIM SDKで、クライアントアプリケーションに組み込むためのものです。主な機能とモジュールは以下の通りです: + ++ 🌟 主な機能: + + - 📦 ローカルストレージ + - 🔔 リスナーコールバック + - 🛡️ APIのラッピング + - 🌐 接続管理 + + ## 📚 主なモジュール: + + 1. 🚀 初初期化とログイン + 2. 👤 ユーザー管理 + 3. 👫 友達管理 + 4. 🤖 グループ機能 + 5. 💬 会話処理 + +Golangを使用して構築され、クロスプラットフォームの導入をサポートし、すべてのプラットフォームで一貫したアクセス体験を提供します。 + +👉 **[GO SDKを探索する](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 OpenIMServerについて + ++ **OpenIMServer** には以下の特徴があります: + - 🌐 マイクロサービスアーキテクチャ:クラスターモードをサポートし、ゲートウェイ(gateway)と複数のrpcサービスを含みます。 + - 🚀 多様なデプロイメント方法:ソースコード、kubernetes、またはdockerでのデプロイメントをサポートします。 + - 海量ユーザーサポート:十万人規模の超大型グループ、千万人のユーザー、および百億のメッセージ + +### 強化されたビジネス機能: + ++ **REST API**:OpenIMServerは、ビジネスシステム用のREST APIを提供しており、ビジネスにさらに多くの機能を提供することを目指しています。たとえば、バックエンドインターフェースを通じてグループを作成したり、プッシュメッセージを送信したりするなどです。 ++ **Webhooks**:OpenIMServerは、より多くのビジネス形態を拡張するためのコールバック機能を提供しています。コールバックとは、特定のイベントが発生する前後に、OpenIMServerがビジネスサーバーにリクエストを送信することを意味します。例えば、メッセージ送信の前後のコールバックなどです。 + +👉 **[もっと詳しく知る](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: 全体のアーキテクチャ + +Open-IM-Serverの機能の核心に迫るために、アーキテクチャダイアグラムをご覧ください。 + +![Overall Architecture](../images/architecture-layers.png) + +## :rocket: クイックスタート + +iOS/Android/H5/PC/Webでのオンライン体験: + +👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)** + +🤲 ユーザー体験を容易にするために、私たちは様々なデプロイメントソリューションを提供しています。以下のリストから、ご自身のデプロイメント方法を選択できます: + ++ **[ソースコードデプロイメントガイド](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker デプロイメントガイド](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes デプロイメントガイド](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Mac 開発者向けデプロイメントガイド](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: OpenIMの開発を始める + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM 私たちの目標は、トップレベルのオープンソースコミュニティを構築することです。[コミュニティリポジトリ](https://github.com/OpenIMSDK/community)には一連の基準があります。 + +このOpen-IM-Serverリポジトリに貢献したい場合は、[貢献者ドキュメントをお読みください](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。 + +始める前に、変更に必要があることを確認してください。最良の方法は、[新しいディスカッション](https://github.com/openimsdk/open-im-server/discussions/new/choose)や[Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)での通信を作成すること、または問題を発見した場合は、まずそれを[報告](https://github.com/openimsdk/open-im-server/issues/new/choose)することです。 + +- [OpenIM APIリファレンス](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash ロギング](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD アクション](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [OpenIM コード規約](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [OpenIM コミットガイドライン](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [OpenIM 開発ガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [OpenIM ディレクトリ構造](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [OpenIM 環境設定](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [OpenIM エラーコードリファレンス](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git ワークフロー](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git チェリーピックガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub ワークフロー](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [OpenIM Go コード基準](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [OpenIM 画像ガイドライン](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [OpenIM 初期設定](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [OpenIM Docker インストールガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [OpenIM Linux システムインストール](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux 開発ガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [OpenIM ローカルアクションガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM ロギング規約](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [OpenIM オフラインデプロイメント](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc ツール](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [OpenIM テスティングガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM ユーティリティGo](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile ユーティリティ](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OpenIM スクリプトユーティリティ](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM バージョニング](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [バックエンド管理とモニターデプロイメント](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [OpenIM用Mac開発者デプロイメントガイド](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: コミュニティ + ++ 📚 [OpenIM コミュニティ](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM 興味グループ](https://github.com/Openim-sigs) ++ 🚀 [私たちのSlackコミュニティに参加する](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [私たちのWeChat(微信群)に参加する](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: コミュニティミーティング + +私たちは、誰もがコミュニティに参加し、コードに貢献してもらいたいと考えています。私たちは、ギフトや報酬を提供し、毎週木曜日の夜に参加していただくことを歓迎します。 + +私たちの会議は[OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)🎯で行われます。そこでOpen-IM-Serverパイプラインを検索して参加できます。 + + +私たちは[隔週の会議](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting)のメモを[GitHubディスカッション](https://github.com/openimsdk/open-im-server/discussions/categories/meeting)に記録しています。歴史的な会議のメモや会議のリプレイは[Google Docs📑](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing)で利用可能です。 + +## :eyes: OpenIMを使用している人たち + +プロジェクトユーザーのリストについては、[ユーザーケーススタディ](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md)ページをご覧ください。[コメント📝](https://github.com/openimsdk/open-im-server/issues/379)を残して、あなたの使用例を共有することを躊躇しないでください。 + +## :page_facing_up: ライセンス + +OpenIMはApache 2.0ライセンスの下でライセンスされています。完全なライセンステキストについては、[LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE)を参照してください。 + +このリポジトリに表示される[OpenIM](https://github.com/openimsdk/open-im-server)ロゴ、そのバリエーション、およびアニメーションバージョン([assets/logo](./assets/logo)および[assets/logo-gif](assets/logo-gif)ディレクトリ内)は、著作権法によって保護されています。 + +## 🔮 貢献者の皆様に感謝します! + + + + \ No newline at end of file From 5cb69b874207713338e66af8c4a410fcee10cd47 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Tue, 6 Feb 2024 15:51:25 +0800 Subject: [PATCH 18/74] cicd: bump League Patch (#1892) --- internal/msgtransfer/init.go | 7 ++++--- pkg/common/kafka/consumer_group.go | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 65a6b1935..65518c324 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,22 +18,23 @@ import ( "context" "errors" "fmt" + "net/http" "os" "os/signal" + "sync" "syscall" "time" - "net/http" - "sync" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mw" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 3f444cc1f..5bff50d88 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,10 +17,12 @@ package kafka import ( "context" "errors" - "github.com/IBM/sarama" "strings" + + "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) From 49610b56040ca57e334a55774037b47b118c7eb0 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Tue, 6 Feb 2024 15:53:03 +0800 Subject: [PATCH 19/74] Fix Script Error and Enhance Code Robustness and Details (#1890) * feat: add openim deployment tactics Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: add openim deployment tactics Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: set openim admin chat code Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: set openim admin chat code Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * fix: fix openim msgtransfer code * fix: fix openim msgtransfer code --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- .devcontainer/README.md | 140 -------------------------- .devcontainer/devcontainer.json | 72 ------------- cmd/openim-api/main.go | 1 + pkg/common/cmd/api.go | 3 - pkg/common/cmd/msg_transfer.go | 3 +- pkg/common/cmd/root.go | 5 +- pkg/common/config/parse.go | 2 +- scripts/install/openim-api.sh | 46 ++++----- scripts/install/openim-crontask.sh | 2 +- scripts/install/openim-msggateway.sh | 2 +- scripts/install/openim-msgtransfer.sh | 16 +-- scripts/install/openim-push.sh | 2 +- scripts/install/openim-rpc.sh | 2 +- scripts/lib/util.sh | 6 +- scripts/stop-all.sh | 9 +- 15 files changed, 43 insertions(+), 268 deletions(-) delete mode 100644 .devcontainer/README.md delete mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/README.md b/.devcontainer/README.md deleted file mode 100644 index 24778f8ee..000000000 --- a/.devcontainer/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# OpenIM - OSS Development Container - -[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/openimsdk/open-im-server) - -This repository includes configuration for a development container for working with OpenIM - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces). - -> **Tip:** The default VNC password is `openIM123`. The VNC server runs on port `5901` and a web client is available on port `11001`, openim-admin on port `11002`. - -## Quick start - local - -If you already have VS Code and Docker installed, you can click the badge above or [here](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/openimsdk/open-im-server) to get started. Clicking these links will cause VS Code to automatically install the Dev Containers extension if needed, clone the source code into a container volume, and spin up a dev container for use. - -1. Install Docker Desktop or Docker for Linux on your local machine. (See [docs](https://aka.ms/vscode-remote/containers/getting-started) for additional details.) - -2. **Important**: Docker needs at least **4 Cores and 8 GB of RAM** to run a full build with **9 GB of RAM** being recommended. If you are on macOS, or are using the old Hyper-V engine for Windows, update these values for Docker Desktop by right-clicking on the Docker status bar item and going to **Preferences/Settings > Resources > Advanced**. - - > **Note:** The [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) extension is included in the container so you can keep an eye on CPU/Memory in the status bar. - -3. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [Dev Containers](https://aka.ms/vscode-remote/download/containers) extension. - - ![Image of Dev Containers extension](https://microsoft.github.io/vscode-remote-release/images/dev-containers-extn.png) - - > **Note:** The Dev Containers extension requires the Visual Studio Code distribution of OpenIM - OSS. See the [FAQ](https://aka.ms/vscode-remote/faq/license) for details. - -4. Press Ctrl/Cmd + Shift + P or F1 and select **Dev Containers: Clone Repository in Container Volume...**. - - > **Tip:** While you can use your local source tree instead, operations like `yarn install` can be slow on macOS or when using the Hyper-V engine on Windows. We recommend using the WSL filesystem on Windows or the "clone repository in container" approach on Windows and macOS instead since it uses "named volume" rather than the local filesystem. - -5. Type `https://github.com/openimsdk/open-im-server` (or a branch or PR URL) in the input box and press Enter. - -6. After the container is running: - 1. If you have the `DISPLAY` or `WAYLAND_DISPLAY` environment variables set locally (or in WSL on Windows), desktop apps in the container will be shown in local windows. - 2. If these are not set, open a web browser and go to [http://localhost:11001](http://localhost:11001), or use a [VNC Viewer][def] to connect to `localhost:11001` and enter `vscode` as the password. Anything you start in VS Code, or the integrated terminal, will appear here. - -Next: **[Try it out!](#try-it)** - -## Quick start - GitHub Codespaces - -1. From the [openimsdk/open-im-server GitHub repository](https://github.com/openimsdk/open-im-server), click on the **Code** dropdown, select **Open with Codespaces**, and then click on **New codespace**. If prompted, select the **Standard** machine size (which is also the default). - - > **Note:** You will not see these options within GitHub if you are not in the Codespaces beta. - -2. After the codespace is up and running in your browser, press Ctrl/Cmd + Shift + P or F1 and select **Ports: Focus on Ports View**. - -3. You should see **VNC web client (11001)** under in the list of ports. Select the line and click on the globe icon to open it in a browser tab. - - > **Tip:** If you do not see the port, Ctrl/Cmd + Shift + P or F1, select **Forward a Port** and enter port `11001`. - -4. In the new tab, you should see noVNC. Click **Connect** and enter `vscode` as the password. - -Anything you start in VS Code, or the integrated terminal, will appear here. - -Next: **[Try it out!](#try-it)** - -### Using VS Code with GitHub Codespaces - -You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer][def]. Here's how to do it. - -1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces). - - > **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of OpenIM - OSS. - -2. After the VS Code is up and running, press Ctrl/Cmd + Shift + P or F1, choose **Codespaces: Create New Codespace**, and use the following settings: - -- `openimsdk/open-im-server` for the repository. -- Select any branch (e.g. **main**) - you can select a different one later. -- Choose **Standard** (4-core, 8GB) as the size. - -3. After you have connected to the codespace, you can use a [VNC Viewer][def] to connect to `localhost:5901` and enter `vscode` as the password. - - > **Tip:** You may also need change your VNC client's **Picture Quality** setting to **High** to get a full color desktop. - -4. Anything you start in VS Code, or the integrated terminal, will appear here. - -Next: **[Try it out!](#try-it)** - -## Try it - -This container uses the [Fluxbox](http://fluxbox.org/) window manager to keep things lean. **Right-click on the desktop** to see menu options. It works with GNOME and GTK applications, so other tools can be installed if needed. - - > **Note:** You can also set the resolution from the command line by typing `set-resolution`. - -To start working with OpenIM - OSS, follow these steps: - -1. In your local VS Code client, open a terminal (Ctrl/Cmd + Shift + \`) and type the following commands: - - ```bash - yarn install - bash scripts/code.sh - ``` - -2. After the build is complete, open a web browser or a [VNC Viewer][def] to connect to the desktop environment as described in the quick start and enter `vscode` as the password. - -3. You should now see OpenIM - OSS! - -Next, let's try debugging. - -1. Shut down OpenIM - OSS by clicking the box in the upper right corner of the OpenIM - OSS window through your browser or VNC viewer. - -2. Go to your local VS Code client, and use the **Run / Debug** view to launch the **VS Code** configuration. (Typically the default, so you can likely just press F5). - - > **Note:** If launching times out, you can increase the value of `timeout` in the "VS Code", "Attach Main Process", "Attach Extension Host", and "Attach to Shared Process" configurations in [launch.json](../../.vscode/launch.json). However, running `./scripts/code.sh` first will set up Electron which will usually solve timeout issues. - -3. After a bit, OpenIM - OSS will appear with the debugger attached! - -Enjoy! - - -### Dotfiles - -Dotfiles are files and folders on Unix-like systems starting with `.` that control the configuration of applications and shells on your system. You can store and manage your dotfiles in a repository on GitHub. For advice and tutorials about what to include in your dotfiles repository, see [GitHub does dotfiles](https://dotfiles.github.io/). - -Your dotfiles repository might include your shell aliases and preferences, any tools you want to install, or any other codespace personalization you want to make. - -You can configure GitHub Codespaces to use dotfiles from any repository you own by selecting that repository in your [personal GitHub Codespaces settings](https://github.com/settings/codespaces). - -When you create a new codespace, GitHub clones your selected dotfiles repository to the codespace environment, and looks for one of the following files to set up the environment. - -- *install.sh* -- *install* -- *bootstrap.sh* -- *bootstrap* -- *script/bootstrap* -- *setup.sh* -- *setup* -- *script/setup* - -If none of these files are found, then any files or folders in your selected dotfiles repository starting with `.` are symlinked to the codespace's `~` or `$HOME` directory. - -Any changes to your selected dotfiles repository will apply only to each new codespace, and do not affect any existing codespace. - -**Note:** Currently, Codespaces does not support personalizing the User-scoped settings for VS Code with your `dotfiles` repository. You can set default Workspace and Remote [Codespaces] settings for a specific project in the project's repository. For more information, see "[Introduction to dev containers](https://docs.github.com/en/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers#creating-a-custom-dev-container-configuration)." - -In addition, you can also configure Codespaces secrets on your personal profile page at [github.com/settings/codespaces](https://github.com/settings/codespaces). Development environment secrets are environment variables that are encrypted, and they are accessible to any codespace you create using repositories that have access to these secrets. - -### Notes - -The container comes with VS Code Insiders installed. To run it from an Integrated Terminal use `VSCODE_IPC_HOOK_CLI= /usr/bin/code-insiders .`. - -[def]: https://www.realvnc.com/en/connect/download/viewer/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 69a35f66a..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - // Reference Doc: https://code.visualstudio.com/remote/advancedcontainers/overview - "name": "OpenIM Dev Environment", - // Update the container version when you publish dev-container - "build": { "dockerfile": "Dockerfile" }, - // Replace with uncommented line below to build your own local copy of the image - // "dockerFile": "../docker/Dockerfile-dev", - "remoteEnv": { - "GO111MODULE": "on", - "GOPROXY": "https://goproxy.cn", - "GOSUMDB": "sum.golang.org", - "GONOPROXY": "github.com/openimsdk", - "GONOSUMDB": "github.com/openimsdk", - "GOPRIVATE": "github.com/openimsdk" - }, - "customizations": { - "vscode": { - "extensions": [ - "davidanson.vscode-markdownlint", - "golang.go", - "ms-azuretools.vscode-dapr", - "ms-azuretools.vscode-docker", - "ms-kubernetes-tools.vscode-kubernetes-tools" - ], - "settings": { - "go.toolsManagement.checkForUpdates": "local", - "go.useLanguageServer": true, - "go.gopath": "/go" - } - } - }, - "mounts": [ - // Mount docker-in-docker library volume - "type=volume,source=dind-var-lib-docker,target=/var/lib/docker", - - // Bind mount docker socket under an alias to support docker-from-docker - "type=bind,source=/var/run/docker.sock,target=/var/run/docker-host.sock", - - // Bind mount docker socket under an alias to support docker-from-docker - // "type=bind,source=${env:HOME}${env:USERPROFILE}/.minikube/cache,target=/home/openim/.minikube/cache", - - // Uncomment to clone local .kube/config into devcontainer - "type=bind,source=${env:HOME}${env:USERPROFILE}/.kube,target=/home/openim/.kube-localhost" - - // Uncomment to additionally clone minikube certs into devcontainer for use with .kube/config - // "type=bind,source=${env:HOME}${env:USERPROFILE}/.minikube,target=/home/openim/.minikube-localhost" - ], - // Always run image-defined default command - "overrideCommand": false, - // On Linux, this will prevent new files getting created as root, but you - // may need to update the USER_UID and USER_GID in docker/Dockerfile-dev - // to match your user if not 1000. - // "remoteUser": "openimsdk", - "runArgs": [ - // Enable ptrace-based debugging for go - "--cap-add=SYS_PTRACE", - "--security-opt", - "seccomp=unconfined", - - // Uncomment to bind to host network for local devcontainer; this is necessary if using the - // bind-mounted /var/run/docker-host.sock directly. - "--net=host", - - // Enable docker-in-docker configuration. Comment out if not using for better security. - "--privileged", - - // Run the entrypoint defined in container image. - "--init" - ], - "workspaceFolder": "/workspaces/openim", - "workspaceMount": "type=bind,source=${localWorkspaceFolder},target=/workspaces/openim" -} diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index a45bcbdd8..bbb5eb968 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -43,6 +43,7 @@ import ( func main() { apiCmd := cmd.NewApiCmd() apiCmd.AddPortFlag() + apiCmd.AddPrometheusPortFlag() apiCmd.AddApi(run) if err := apiCmd.Execute(); err != nil { fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index 00c6cb241..db1f488ad 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -15,8 +15,6 @@ package cmd import ( - "fmt" - "github.com/OpenIMSDK/protocol/constant" "github.com/spf13/cobra" @@ -41,7 +39,6 @@ func (a *ApiCmd) AddApi(f func(port int, promPort int) error) { } func (a *ApiCmd) GetPortFromConfig(portType string) int { - fmt.Println("GetPortFromConfig:", portType) if portType == constant.FlagPort { return config2.Config.Api.OpenImApiPort[0] } else if portType == constant.FlagPrometheusPort { diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index f99b625c7..e57bab89d 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -47,7 +47,6 @@ func (m *MsgTransferCmd) Exec() error { } func (m *MsgTransferCmd) GetPortFromConfig(portType string) int { - fmt.Println("GetPortFromConfig:", portType) if portType == constant.FlagPort { return 0 } else if portType == constant.FlagPrometheusPort { @@ -56,9 +55,11 @@ func (m *MsgTransferCmd) GetPortFromConfig(portType string) int { } return 0 } + func (m *MsgTransferCmd) AddTransferProgressFlag() { m.Command.Flags().IntP(constant.FlagTransferProgressIndex, "n", 0, "transfer progress index") } + func (m *MsgTransferCmd) getTransferProgressFlagValue() int { nindex, err := m.Command.Flags().GetInt(constant.FlagTransferProgressIndex) if err != nil { diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 98ca8f892..eab4a32bc 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -163,7 +163,7 @@ func (r *RootCmd) GetPrometheusPortFlag() int { func (r *RootCmd) getConfFromCmdAndInit(cmdLines *cobra.Command) error { configFolderPath, _ := cmdLines.Flags().GetString(constant.FlagConf) - fmt.Println("configFolderPath:", configFolderPath) + fmt.Println("The directory of the configuration file to start the process:", configFolderPath) return config2.InitConfig(configFolderPath) } @@ -176,10 +176,9 @@ func (r *RootCmd) AddCommand(cmds ...*cobra.Command) { } func (r *RootCmd) GetPortFromConfig(portType string) int { - fmt.Println("RootCmd.GetPortFromConfig:", portType) return 0 } + func (r *RootCmd) PortFromConfig(portType string) int { - fmt.Println("PortFromConfig:", portType) return r.cmdItf.GetPortFromConfig(portType) } diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 64719d6a1..4037429e3 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -101,7 +101,7 @@ func initConfig(config any, configName, configFolderPath string) error { if err = yaml.Unmarshal(data, config); err != nil { return fmt.Errorf("unmarshal yaml error: %w", err) } - fmt.Println("use config", configFolderPath) + fmt.Println("The path of the configuration file to start the process:", configFolderPath) return nil } diff --git a/scripts/install/openim-api.sh b/scripts/install/openim-api.sh index c81dfcd0d..be2a2d33b 100755 --- a/scripts/install/openim-api.sh +++ b/scripts/install/openim-api.sh @@ -33,8 +33,12 @@ readonly OPENIM_API_SERVICE_TARGETS=( ) readonly OPENIM_API_SERVICE_LISTARIES=("${OPENIM_API_SERVICE_TARGETS[@]##*/}") -function openim::api::start() { +readonly OPENIM_API_PROMETHEUS_PORT_TARGETS=( + ${API_PROM_PORT} +) +readonly OPENIM_API_PROMETHEUS_PORT_LISTARIES=("${OPENIM_API_PROMETHEUS_PORT_TARGETS[@]##*/}") +function openim::api::start() { rm -rf "$TMP_LOG_FILE" echo "++ OPENIM_API_SERVICE_LISTARIES: ${OPENIM_API_SERVICE_LISTARIES[@]}" @@ -47,34 +51,20 @@ function openim::api::start() { printf "| Service Name | Port |\n" printf "+------------------------+--------------+\n" - length=${#OPENIM_API_SERVICE_LISTARIES[@]} + local length=${#OPENIM_API_SERVICE_LISTARIES[@]} - for ((i=0; i<$length; i++)); do + for ((i=0; i> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + + # Append Prometheus port argument if specified + if [ -n "${prometheus_port}" ]; then + cmd+=" --prometheus_port ${prometheus_port}" + fi + + echo "Starting service with command: $cmd" + + nohup $cmd >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & if [ $? -ne 0 ]; then openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}." diff --git a/scripts/install/openim-crontask.sh b/scripts/install/openim-crontask.sh index 6068e97d5..395b27cd1 100755 --- a/scripts/install/openim-crontask.sh +++ b/scripts/install/openim-crontask.sh @@ -54,7 +54,7 @@ function openim::crontask::start() { openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}" - nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & openim::util::check_process_names ${SERVER_NAME} } diff --git a/scripts/install/openim-msggateway.sh b/scripts/install/openim-msggateway.sh index 4e591deca..a2316c784 100755 --- a/scripts/install/openim-msggateway.sh +++ b/scripts/install/openim-msggateway.sh @@ -64,7 +64,7 @@ function openim::msggateway::start() { PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" fi - nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done openim::util::check_process_names ${SERVER_NAME} diff --git a/scripts/install/openim-msgtransfer.sh b/scripts/install/openim-msgtransfer.sh index def22c38b..783e06729 100755 --- a/scripts/install/openim-msgtransfer.sh +++ b/scripts/install/openim-msgtransfer.sh @@ -38,7 +38,7 @@ function openim::msgtransfer::start() { # Message Transfer Prometheus port list MSG_TRANSFER_PROM_PORTS=(openim::util::list-to-string ${MSG_TRANSFER_PROM_PORT} ) - + openim::log::status "OpenIM Prometheus ports: ${MSG_TRANSFER_PROM_PORTS[*]}" openim::log::status "OpenIM Msggateway config path: ${OPENIM_MSGTRANSFER_CONFIG}" @@ -54,12 +54,14 @@ function openim::msgtransfer::start() { fi for (( i=0; i<$OPENIM_MSGGATEWAY_NUM; i++ )) do - openim::log::info "prometheus port: ${MSG_TRANSFER_PROM_PORTS[$i]}" - PROMETHEUS_PORT_OPTION="" - if [[ -n "${OPENIM_PROMETHEUS_PORTS[$i]}" ]]; then - PROMETHEUS_PORT_OPTION="--prometheus_port ${OPENIM_PROMETHEUS_PORTS[$i]}" - fi - nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + openim::log::info "prometheus port: ${MSG_TRANSFER_PROM_PORTS[$i]}" + PROMETHEUS_PORT_OPTION="" + if [[ -n "${MSG_TRANSFER_PROM_PORTS[$i+1]}" ]]; then + PROMETHEUS_MSG_TRANSFER_PORT="${MSG_TRANSFER_PROM_PORTS[$i+1]%,}" + openim::util::stop_services_on_ports ${PROMETHEUS_MSG_TRANSFER_PORT} + PROMETHEUS_PORT_OPTION="--prometheus_port ${PROMETHEUS_MSG_TRANSFER_PORT}" + fi + nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done openim::util::check_process_names "${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}" diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh index 4d14ca675..ab12735c1 100755 --- a/scripts/install/openim-push.sh +++ b/scripts/install/openim-push.sh @@ -73,7 +73,7 @@ function openim::push::start() { for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" - nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done openim::util::check_process_names ${SERVER_NAME} diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh index 00031f211..f8feaaa57 100755 --- a/scripts/install/openim-rpc.sh +++ b/scripts/install/openim-rpc.sh @@ -160,7 +160,7 @@ function openim::rpc::start_service() { printf "Specifying prometheus port: %s\n" "${prometheus_port}" cmd="${cmd} --prometheus_port ${prometheus_port}" fi - nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE") & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & } ###################################### Linux Systemd ###################################### diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index cace53645..7acb1fcdd 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -505,8 +505,6 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes were successfully stopped. if [[ ${#stopped[@]} -ne 0 ]]; then - echo - openim::log::info "Stopped services on ports:" for port in "${stopped[@]}"; do openim::log::info "Successfully stopped service on port $port." done @@ -1741,8 +1739,6 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes were successfully stopped. if [[ ${#stopped[@]} -ne 0 ]]; then - echo - openim::log::info "Stopped services on ports:" for port in "${stopped[@]}"; do openim::log::info "Successfully stopped service on port $port." done @@ -2828,4 +2824,4 @@ function openim::util::gen_os_arch() { if [[ "$*" =~ openim::util:: ]];then eval $* -fi +fi \ No newline at end of file diff --git a/scripts/stop-all.sh b/scripts/stop-all.sh index 1d2eddd78..2acb9cdc5 100755 --- a/scripts/stop-all.sh +++ b/scripts/stop-all.sh @@ -36,11 +36,4 @@ echo -e "\n++ Stop all processes in the path ${OPENIM_OUTPUT_HOSTBIN}" openim::util::stop_services_with_name "${OPENIM_OUTPUT_HOSTBIN}" -echo -n "Stopping services 15 seconds." -for i in {1..15}; do - echo -n "." - sleep 1 -done -echo -e "\nServices stopped." - -openim::log::success "✨ Wait 15 seconds for all processes to be killed" \ No newline at end of file +openim::log::success "✨ All processes to be killed" \ No newline at end of file From 51faa9190d3c98ac6245b93a29f73b1a6e29944a Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:50:17 +0800 Subject: [PATCH 20/74] docs: add README.md-TR (#1900) Signed-off-by: wxuanF <2569456943@qq.com> --- docs/readme/README-TR.md | 189 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 docs/readme/README-TR.md diff --git a/docs/readme/README-TR.md b/docs/readme/README-TR.md new file mode 100644 index 000000000..ff545e644 --- /dev/null +++ b/docs/readme/README-TR.md @@ -0,0 +1,189 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ + +## Ⓜ️ OpenIM Hakkında + +OpenIM, uygulamalara sohbet, sesli-görüntülü aramalar, bildirimler ve AI sohbet robotları entegre etmek için özel olarak tasarlanmış bir hizmet platformudur. Güçlü API'ler ve Webhook'lar sunarak, geliştiricilerin bu etkileşimli özellikleri uygulamalarına kolayca dahil etmelerini sağlar. OpenIM bağımsız bir sohbet uygulaması değildir, ancak zengin iletişim işlevselliği sağlama amacıyla diğer uygulamaları destekleyen bir platform olarak hizmet verir. Aşağıdaki diyagram, AppServer, AppClient, OpenIMServer ve OpenIMSDK arasındaki etkileşimi detaylandırmak için açıklar. + + + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 OpenIMSDK Hakkında + + **OpenIMSDK**, müşteri uygulamalarına gömülmek üzere özel olarak oluşturulan **OpenIMServer** için tasarlanmış bir IM SDK'sıdır. Ana özellikleri ve modülleri aşağıdaki gibidir: + ++ 🌟 Ana Özellikler: + + - 📦 Yerel depolama + - 🔔 Dinleyici geri çağırmaları + - 🛡️ API sarımı + - 🌐 Bağlantı yönetimi + + ## 📚 Ana Modüller: + + 1. 🚀 Başlatma ve Giriş + 2. 👤 Kullanıcı Yönetimi + 3. 👫 Arkadaş Yönetimi + 4. 🤖 Grup Fonksiyonları + 5. 💬 Konuşma Yönetimi + +Golang kullanılarak inşa edilmiş ve tüm platformlarda tutarlı bir erişim deneyimi sağlayacak şekilde çapraz platform dağıtımını destekler. + +👉 **[GO SDK Keşfet](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 OpenIMServer Hakkında + ++ **OpenIMServer** aşağıdaki özelliklere sahiptir: + - 🌐 Mikroservis mimarisi: Bir kapı ve çoklu rpc servisleri içeren küme modunu destekler. + - 🚀 Çeşitli dağıtım yöntemleri: Kaynak kodu, Kubernetes veya Docker aracılığıyla dağıtımı destekler. + - Büyük kullanıcı tabanı desteği: Yüz binlerce kullanıcısı olan süper büyük gruplar, on milyonlarca kullanıcı ve milyarlarca mesaj. + +### Geliştirilmiş İşlevsellik: + ++ **REST API**:OpenIMServer, işletmeleri gruplar oluşturma ve arka plan arayüzleri aracılığıyla itme mesajları gönderme gibi daha fazla işlevsellikle güçlendirmeyi amaçlayan iş sistemleri için REST API'leri sunar. ++ **Webhooks**:OpenIMServer, daha fazla iş formunu genişletme yetenekleri sağlayan geri çağırma özellikleri sunar. Geri çağırma, OpenIMServer'ın belirli bir olaydan önce veya sonra, örneğin bir mesaj göndermeden önce veya sonra iş sunucusuna bir istek göndermesi anlamına gelir. + +👉 **[Daha fazla bilgi edinin](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Genel Mimarisi + +Mimari diyagramımızla Open-IM-Server'ın işlevselliğinin kalbine dalın. + +![Overall Architecture](../images/architecture-layers.png) + +## :rocket: Hızlı Başlangıç + +Birçok platformu destekliyoruz. Web tarafında hızlı deneyim için adresler şunlardır: + +👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)** + +🤲 Kullanıcı deneyimini kolaylaştırmak için çeşitli dağıtım çözümleri sunuyoruz. Aşağıdaki listeden dağıtım yönteminizi seçebilirsiniz: + ++ **[Kaynak Kodu Dağıtım Kılavuzu](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker Dağıtım Kılavuzu](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes Dağıtım Kılavuzu](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Mac Geliştirici Dağıtım Kılavuzu](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: OpenIM Geliştirmeye Başlamak + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM Amacımız, üst düzey bir açık kaynak topluluğu oluşturmaktır. [Topluluk deposunda](https://github.com/OpenIMSDK/community) bir dizi standartımız var. + +Bu Open-IM-Server deposuna katkıda bulunmak istiyorsanız, lütfen katkıda bulunanlar için [dokümantasyonumuzu](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md) okuyun. + +Başlamadan önce, lütfen değişikliklerinizin talep edildiğinden emin olun. Bunun için en iyisi, [yeni bir tartışma OLUŞTURMAK](https://github.com/openimsdk/open-im-server/discussions/new/choose) veya [Slack İletişimi](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) kurmak, ya da bir sorun bulursanız, önce bunu [rapor](https://github.com/openimsdk/open-im-server/issues/new/choose) etmektir. + +- [OpenIM API Referansı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash Günlüğü](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD İşlemleri](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [OpenIM Kod Kuralları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [OpenIM Taahhüt Kuralları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [OpenIM Geliştirme Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [OpenIM Dizin Yapısı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [OpenIM Ortam Kurulumu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [OpenIM Hata Kodu Referansı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git İş Akışı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git Cherry Pick Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub İş Akışı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [OpenIM Go Kod Standartları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [OpenIM Görüntü Kuralları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [OpenIM İlk Yapılandırma](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [OpenIM Docker Kurulum Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [OpenIM Linux Sistem Kurulumu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux Geliştirme Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [OpenIM Yerel İşlemler Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM Günlük Kuralları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [OpenIM Çevrimdışı Dağıtım](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc Araçları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [OpenIM Test Kılavuzu](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM Yardımcı Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile Yardımcı Programları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OOpenIM Betik Yardımcı Programları](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM Sürümleme](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Arka uç yönetimi ve izleme dağıtımı](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Mac Geliştirici Dağıtım Kılavuzu for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Topluluk + ++ 📚 [OpenIM Topluluğu](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM İlgi Grubu](https://github.com/Openim-sigs) ++ 🚀 [Slack topluluğumuza katılın](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Wechat grubumuza katılın (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Topluluk Toplantıları + +Topluluğumuza herkesin katılmasını ve kod katkısında bulunmasını istiyoruz, hediyeler ve ödüller sunuyoruz ve sizi her Perşembe gecesi bize katılmaya davet ediyoruz. + +Konferansımız [OpenIM Slack'te](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, ardından Open-IM-Server boru hattını arayıp katılabilirsiniz. + +İki haftada bir yapılan toplantının [notlarını](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) [GitHub tartışmalarında alıyoruz](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Tarihi toplantı notlarımız ve toplantıların tekrarları [Google Docs'ta](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing) 📑 mevcut. + +## :eyes: Kimler OpenIM Kullanıyor + +Proje kullanıcılarının bir listesi için [kullanıcı vaka çalışmaları](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) sayfamıza göz atın. Bir 📝[yorum](https://github.com/openimsdk/open-im-server/issues/379) bırakmaktan ve kullanım durumunuzu paylaşmaktan çekinmeyin. + +## :page_facing_up: Lisans + +OpenIM, Apache 2.0 lisansı altında lisanslanmıştır. Tam lisans metni için [LICENSE'ı](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) görün. + +Bu depoda, [assets/logo](../../assets/logo) ve [assets/logo-gif](../../assets/logo-gif) dizinlerinde görüntülenen [OpenIM](https://github.com/openimsdk/open-im-server) logosu, çeşitleri ve animasyonlu versiyonları, telif hakkı yasaları tarafından korunmaktadır. + +## 🔮 Katkıda bulunanlarımıza teşekkürler! + + + + \ No newline at end of file From 9dd44a75d0015474cbe295e106ecbe7465f3d3ab Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:51:45 +0800 Subject: [PATCH 21/74] docs: README KR (#1895) Signed-off-by: wxuanF <2569456943@qq.com> --- docs/readme/README-KR.md | 192 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 docs/readme/README-KR.md diff --git a/docs/readme/README-KR.md b/docs/readme/README-KR.md new file mode 100644 index 000000000..f7efc71a5 --- /dev/null +++ b/docs/readme/README-KR.md @@ -0,0 +1,192 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ + +## Ⓜ️ OpenIM에 대하여 + +OpenIM은 채팅, 오디오-비디오 통화, 알림 및 AI 챗봇을 애플리케이션에 통합하기 위해 특별히 설계된 서비스 플랫폼입니다. 이 플랫폼은 강력한 API와 웹훅을 제공하여 개발자가 이러한 상호작용 기능을 애플리케이션에 쉽게 통합할 수 있게 합니다. OpenIM은 독립 실행형 채팅 애플리케이션이 아니라, 다른 애플리케이션들이 풍부한 커뮤니케이션 기능을 달성할 수 있도록 지원하는 플랫폼으로서의 역할을 합니다. 다음 다이어그램은 AppServer, AppClient, OpenIMServer, 및 OpenIMSDK 간의 상호작용을 자세히 설명하기 위해 제시되었습니다. + + + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 OpenIMSDK에 대하여 + + **OpenIMSDK**는**OpenIMServer**를 위해 특별히 제작된 IM SDK로, 클라이언트 애플리케이션 내에 내장하기 위해 설계되었습니다. 그 주요 기능 및 모듈은 다음과 같습니다: + + ++ 🌟 주요 기능: + + - 📦 로컬 스토리지 + - 🔔 리스너 콜백 + - 🛡️ API 래핑 + - 🌐 연결 관리 + + ## 📚 주요 모듈: + + 1. 🚀 초기화 및 로그인 + 2. 👤 사용자 관리 + 3. 👫 친구 관리 + 4. 🤖 그룹 기능 + 5. 💬 대화 처리 + +이는 Golang을 사용하여 구축되었으며, 모든 플랫폼에서 일관된 접근 경험을 보장하는 크로스 플랫폼 배포를 지원합니다. + +👉 **[GO SDK 탐색하기](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 OpenIMServer에 대하여 + ++ **OpenIMServer** 는 다음과 같은 특성을 가지고 있습니다: + - 🌐 마이크로서비스 아키텍처: 게이트웨이 및 다수의 rpc 서비스를 포함하는 클러스터 모드를 지원합니다. + - 🚀 다양한 배포 방법: 소스 코드, 쿠버네티스 또는 도커를 통한 배포를 지원합니다. + - 대규모 사용자 기반 지원: 수십만 명의 사용자를 포함하는 초대형 그룹, 수천만 명의 사용자 및 수십억 건의 메시지를 지원합니다. + +### 강화된 비즈니스 기능: + ++ **REST API**:OpenIMServer는 비즈니스 시스템을 위한 REST API를 제공하여, 백엔드 인터페이스를 통해 그룹 생성 및 푸시 메시지 전송과 같은 더 많은 기능을 비즈니스에 제공하기 위해 설계되었습니다. ++ **Webhooks**:OpenIMServer는 더 많은 비즈니스 형태를 확장할 수 있는 콜백 기능을 제공합니다. 콜백이란 메시지 전송 전후와 같은 특정 이벤트 전후에 OpenIMServer가 비즈니스 서버로 요청을 보내는 것을 의미합니다. + +👉 **[더 알아보기](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: 전체 아키텍처 + +Open-IM-Server의 기능의 핵심으로 들어가 우리의 아키텍처 다이어그램을 자세히 살펴보세요. + +![Overall Architecture](../images/architecture-layers.png) + +## :rocket: 빠른 시작 + +우리는 많은 플랫폼을 지원합니다. 웹 측에서 빠른 체험을 위한 주소는 다음과 같습니다: + +👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)** + +🤲 사용자 경험을 용이하게 하기 위해, 다양한 배포 솔루션을 제공합니다. 아래 목록에서 배포 방법을 선택할 수 있습니다: + ++ **[소스 코드 배포 가이드](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[docker 배포 가이드](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes 배포 가이드](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Mac 개발자 배포 가이드](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: OpenIM 개발 시작하기 + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM의 목표는 최상위 수준의 오픈 소스 커뮤니티를 구축하는 것입니다. 우리는 [커뮤니티 리포지토리에서](https://github.com/OpenIMSDK/community) 일련의 표준을 가지고 있습니다. + +이 Open-IM-Server 리포지토리에 기여하고 싶다면, 우리의 [기여자 문서](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)를 읽어주세요. + +시작하기 전에, 변경 사항이 필요한지 확인해 주세요. 가장 좋은 방법은 [새로운 토론](https://github.com/openimsdk/open-im-server/discussions/new/choose)을 생성하거나 [Slack 통신을](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 하거나, 문제를 발견했다면 먼저 [보고](https://github.com/openimsdk/open-im-server/issues/new/choose)하는 것입니다. + +- [OpenIM API 참조](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash 로깅](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD 액션](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [OpenIM 코드 규칙](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [OpenIM 커밋 가이드라인](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [OpenIM 개발 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [OpenIM 디렉토리 구조](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [OpenIM 환경 설정](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [OpenIM 오류 코드 참조](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git 작업 흐름](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git 체리 픽 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub 작업 흐름](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [OpenIM Go 코드 표준](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [OpenIM 이미지 가이드라인](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [OpenIM 초기 구성](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [OpenIM docker 설치 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [OpenIM OpenIM Linux 설치](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux 개발 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [OpenIM 로컬 액션 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM 로깅 규칙](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [OpenIM 오프라인 배포](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc 도구](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [OpenIM 테스트 가이드](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM 유틸리티 Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM 메이크파일 유틸리티](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OpenIM 스크립트 유틸리티](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM 버전 관리](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [백엔드 관리 및 모니터 배포](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [맥 개발자 배포 가이드 for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: 커뮤니티 + ++ 📚 [OpenIM 커뮤니티](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM 관심 그룹](https://github.com/Openim-sigs) ++ 🚀 [우리의 Slack 커뮤니티에 가입하기](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [우리의 위챗(微信群)에 가입하기](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: 커뮤니티 미팅 + +우리는 누구나 커뮤니티에 참여하고 코드를 기여할 수 있도록 하며, 선물과 보상을 제공하며, 매주 목요일 밤에 여러분을 환영합니다. + +우리의 회의는 [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯에서 이루어지며, Open-IM-Server 파이프라인을 검색하여 참여할 수 있습니다. + +우리는 격주 회의의 메모를 [GitHub 토론](https://github.com/openimsdk/open-im-server/discussions/categories/meeting)에서 기록하며, 우리의 역사적 [회의](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) 노트와 회의 재생은 [Google Docs 📑](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing)에서 이용할 수 있습니다. + + +## :eyes: OpenIM을 사용하는 사람들 + +프로젝트 사용자 목록을 위한 우리의 [사용자 사례 연구](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) 페이지를 확인하세요. 사용 사례를 공유하고 싶다면 주저하지 말고 [📝코멘트](https://github.com/openimsdk/open-im-server/issues/379)를 남겨주세요. + +## :page_facing_up: 라이선스 + +OpenIM은 Apache 2.0 라이선스에 따라 라이선스가 부여됩니다. 전체 라이선스 텍스트는 [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE)에서 확인할 수 있습니다. + +이 리포지토리 [OpenIM](https://github.com/openimsdk/open-im-server)에 표시된 OpenIM 로고, 그 변형 및 애니메이션 버전은 [assets/logo](../../assets/logo) 및 [assets/logo-gif](../../assets/logo-gif) 디렉토리 아래에 있으며, 저작권 법에 의해 보호됩니다. + + +## 🔮 우리의 기여자들에게 감사합니다! + + + + \ No newline at end of file From 5046b3f6cec2804c4e240918b1f262c4b3988271 Mon Sep 17 00:00:00 2001 From: Seal Bell Date: Wed, 7 Feb 2024 21:52:38 +0800 Subject: [PATCH 22/74] docs:Modified the language naming of files and changed some error paths (#1898) * Add Simplified Chinese README.md * Add Ukrainian README.md * Add Traditional Chinese README.md * Add Czech README.md * Add Hungarian README.md * Add spanish README.md * Add Persian README.md * Add French README.md * Add German README.md * Add Japanese README.md * Add Polish README.md * Add Indonesian README.md * Add Finnish README.md * Add Malayalam README.md * Add Dutch README.md * Add Italian README.md * Add Russian README.md * Added Brazilian Portuguese README.md * Add Esperanto README.md * Add Korean README.md * Add Vietnamese README.md * Add Arabic README.md * Add Danish README.md * Add Greek README.md * Add Turkish README.md * Correct document suffix to distinguish language versions * Improve path and file naming corrections * Correct document suffix to distinguish language versions * Improve path and file naming corrections * Add Ukrainian README_uk.md --- README-zh_CN.md | 48 +++--- README.md | 48 +++--- docs/readme/README-UA.md | 7 - docs/readme/{README-JP.md => README_ja.md} | 48 +++--- docs/readme/README_uk.md | 187 +++++++++++++++++++++ 5 files changed, 259 insertions(+), 79 deletions(-) delete mode 100644 docs/readme/README-UA.md rename docs/readme/{README-JP.md => README_ja.md} (92%) create mode 100644 docs/readme/README_uk.md diff --git a/README-zh_CN.md b/README-zh_CN.md index e33768881..7eabfa509 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

diff --git a/README.md b/README.md index 8721bf2ce..a91024e49 100644 --- a/README.md +++ b/README.md @@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

diff --git a/docs/readme/README-UA.md b/docs/readme/README-UA.md deleted file mode 100644 index 892e16d19..000000000 --- a/docs/readme/README-UA.md +++ /dev/null @@ -1,7 +0,0 @@ -

- - - -

- -
\ No newline at end of file diff --git a/docs/readme/README-JP.md b/docs/readme/README_ja.md similarity index 92% rename from docs/readme/README-JP.md rename to docs/readme/README_ja.md index c1b0eabf5..bd94b1153 100644 --- a/docs/readme/README-JP.md +++ b/docs/readme/README_ja.md @@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

diff --git a/docs/readme/README_uk.md b/docs/readme/README_uk.md new file mode 100644 index 000000000..30bc76730 --- /dev/null +++ b/docs/readme/README_uk.md @@ -0,0 +1,187 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ Про OpenIM + +OpenIM — це сервісна платформа, спеціально розроблена для інтеграції чату, аудіо-відеодзвінків, сповіщень і чат-ботів штучного інтелекту в програми. Він надає ряд потужних API і веб-хуків, що дозволяє розробникам легко включати ці інтерактивні функції у свої програми. OpenIM не є окремою програмою для чату, а скоріше служить платформою для підтримки інших програм у досягненні широких можливостей спілкування. На наступній діаграмі детально показано взаємодію між AppServer, AppClient, OpenIMServer і OpenIMSDK. + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 Про OpenIMSDK + +**OpenIMSDK** – це пакет IM SDK, розроблений для **OpenIMServer**, створений спеціально для вбудовування в клієнтські програми. Його основні функції та модулі такі: + ++ 🌟 Основні характеристики: + + - 📦 Локальне сховище + - 🔔 Зворотні виклики слухача + - 🛡️ Обгортка API + - 🌐 Керування підключенням + ++ 📚 Основні модулі: + + 1. 🚀 Ініціалізація та вхід + 2. 👤 Керування користувачами + 3. 👫 Керування друзями + 4. 🤖 Групові функції + 5. 💬 Ведення розмови + +Він створений за допомогою Golang і підтримує кросплатформне розгортання, забезпечуючи послідовний доступ на всіх платформах. + +👉 **[Дослідити GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Про OpenIMServer + ++ **OpenIMServer** має такі характеристики: + - 🌐 Архітектура мікросервісу: підтримує режим кластера, включаючи шлюз і кілька служб rpc. + - 🚀 Різноманітні методи розгортання: підтримує розгортання через вихідний код, Kubernetes або Docker. + - Підтримка величезної бази користувачів: надвеликі групи із сотнями тисяч користувачів, десятками мільйонів користувачів і мільярдами повідомлень. + +### Розширена бізнес-функціональність: + ++ **REST API**: OpenIMServer пропонує REST API для бізнес-систем, спрямованих на надання компаніям додаткових можливостей, таких як створення груп і надсилання push-повідомлень через серверні інтерфейси. ++ **Веб-перехоплення**: OpenIMServer надає можливості зворотного виклику, щоб розширити більше бізнес-форм. Зворотний виклик означає, що OpenIMServer надсилає запит на бізнес-сервер до або після певної події, як зворотні виклики до або після надсилання повідомлення. + +👉 **[Докладніше](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Загальна архітектура + +Пориньте в серце функціональності Open-IM-Server за допомогою нашої діаграми архітектури. + +![Overall Architecture](../images/architecture-layers.png) + + +## :rocket: Швидкий початок + +Ми підтримуємо багато платформ. Ось адреси для швидкого використання веб-сайту: + +👉 **[Онлайн-демонстрація OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 Щоб полегшити роботу користувача, ми пропонуємо різні рішення для розгортання. Ви можете вибрати спосіб розгортання зі списку нижче: + ++ **[Посібник із розгортання вихідного коду](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Посібник із розгортання Docker](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Посібник із розгортання Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Посібник із розгортання розробника Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Щоб розпочати розробку OpenIM + +[![Відкрити в контейнері для розробників](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM. Наша мета — побудувати спільноту з відкритим кодом найвищого рівня. У нас є набір стандартів у [репозиторії спільноти](https://github.com/OpenIMSDK/community). + +Якщо ви хочете внести свій внесок у це сховище Open-IM-Server, прочитайте нашу [документацію для учасників](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + +Перш ніж почати, переконайтеся, що ваші зміни затребувані. Найкраще для цього створити [нове обговорення](https://github.com/openimsdk/open-im-server/discussions/new/choose) АБО [Нездійснене спілкування](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)або, якщо ви виявите проблему, спершу [повідомити про неї](https://github.com/openimsdk/open-im-server/issues/new/choose). + +- [Довідка щодо API OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Ведення журналу OpenIM Bash](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Дії OpenIM CI/CD](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Положення про код OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Інструкції щодо фіксації OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Посібник з розробки OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Структура каталогу OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Налаштування середовища OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Довідка про код помилки OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Робочий процес OpenIM Git](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [Посібник із вибору OpenIM Git Cherry](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Робочий процес OpenIM GitHub](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [Стандарти коду OpenIM Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Інструкції щодо зображення OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Початкова конфігурація OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Посібник із встановлення OpenIM Docker](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [Встановлення системи OpenIM OpenIM Linux](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [Посібник із розробки OpenIM Linux](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Локальний посібник із дій OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Положення про протоколювання OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Офлайн-розгортання OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Інструменти OpenIM Protoc](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Посібник з тестування OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [Утиліта OpenIM Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [Утиліти OpenIM Makefile](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [Утиліти сценарію OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [Версії OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Керування серверною частиною та моніторинг розгортання](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Посібник із розгортання розробника Mac для OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Спільнота + ++ 📚 [Спільнота OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [Група інтересів OpenIM](https://github.com/Openim-sigs) ++ 🚀 [Приєднайтеся до нашої спільноти Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Приєднайтеся до нашого wechat](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Збори громади + +Ми хочемо, щоб будь-хто долучився до нашої спільноти та додав код, ми пропонуємо подарунки та нагороди, і ми запрошуємо вас приєднатися до нас щочетверга ввечері. + +Наша конференція знаходиться в [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, тоді ви можете шукати конвеєр Open-IM-Server, щоб приєднатися. + +Ми робимо нотатки про кожну [двотижневу зустріч](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting)в [обговореннях GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting). Наші історичні нотатки зустрічей, а також повтори зустрічей доступні в[Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Хто використовує OpenIM + +Перегляньте нашу сторінку [тематичні дослідження користувачів](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md), щоб отримати список користувачів проекту. Не соромтеся залишити [📝коментар](https://github.com/openimsdk/open-im-server/issues/379)і поділитися своїм випадком використання. + +## :page_facing_up: Ліцензія + +OpenIM ліцензовано за ліцензією Apache 2.0. Див. [ЛІЦЕНЗІЯ](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) для повного тексту ліцензії. + +Логотип OpenIM, включаючи його варіації та анімовані версії, що відображаються в цьому сховищі[OpenIM](https://github.com/openimsdk/open-im-server)у каталогах [assets/logo](./assets/logo)і [assets/logo-gif](assets/logo-gif) , захищені законами про авторське право. + +## 🔮 Дякуємо нашим дописувачам! + + + + From 90bd53b7dd1048d65d405b319dd6f76a833ffc8e Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Sun, 18 Feb 2024 17:23:22 +0800 Subject: [PATCH 23/74] docs: renmae README files about other languages (#1915) Signed-off-by: wxuanF <2569456943@qq.com> --- README-zh_CN.md => README_zh_CN.md | 0 docs/readme/{README-KR.md => README_ko.md} | 0 docs/readme/{README-TR.md => README_tr.md} | 48 +++++++++++----------- 3 files changed, 24 insertions(+), 24 deletions(-) rename README-zh_CN.md => README_zh_CN.md (100%) rename docs/readme/{README-KR.md => README_ko.md} (100%) rename docs/readme/{README-TR.md => README_tr.md} (91%) diff --git a/README-zh_CN.md b/README_zh_CN.md similarity index 100% rename from README-zh_CN.md rename to README_zh_CN.md diff --git a/docs/readme/README-KR.md b/docs/readme/README_ko.md similarity index 100% rename from docs/readme/README-KR.md rename to docs/readme/README_ko.md diff --git a/docs/readme/README-TR.md b/docs/readme/README_tr.md similarity index 91% rename from docs/readme/README-TR.md rename to docs/readme/README_tr.md index ff545e644..ca2a816db 100644 --- a/docs/readme/README-TR.md +++ b/docs/readme/README_tr.md @@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

From 5e7138034cb63ea407a19ecc2babd26ae6388a97 Mon Sep 17 00:00:00 2001 From: Seal Bell Date: Sun, 18 Feb 2024 17:36:15 +0800 Subject: [PATCH 24/74] Docs: Modify the wrong file name (#1914) --- docs/readme/README_ko.md | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/readme/README_ko.md b/docs/readme/README_ko.md index f7efc71a5..bd7a1aed3 100644 --- a/docs/readme/README_ko.md +++ b/docs/readme/README_ko.md @@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

From 6186d657e2ef807c65999ae40d2626949bd21c26 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:42:56 +0800 Subject: [PATCH 25/74] feat: add docker-compse-1.yml and cancel the openim-admin annotate in docker-compose.yml (#1881) * fix: del the manager config and manger init statement * fix: fix the Manger judge condition * fix: fix revokeMsg error * fix: find erors * fix: find error * fix: fix the AdminAccount error * fix: del the debug statement * fix: fix the component check func * fix: fix the get zkAddress error * fix: fix the kafka client close error * fix: add env in minio connected * fix: del the minio env * fix: fix the go.mod tools version * fix: del get env in minio conneted * feat: add GetUserToken api and add ex field in GetSortedConversationList resp * fix: fix the go.mod version * fix: add lack method * fix: add a method * fix: add lack implement * fix: fix the tools pkg version * fix: del the unuser pkg * fix: add Limiting judgement of get admin token * feat: add a yml of docker-compose-1.yml * fix: add the lack content in docker-compose-1.yml * fix: add the tips in docker-compose.yml --------- Co-authored-by: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> --- docker-compose-1.yml | 298 +++++++++++++++++++++++ docker-compose.yml | 25 +- internal/rpc/conversation/conversaion.go | 1 - pkg/authverify/token.go | 3 +- 4 files changed, 312 insertions(+), 15 deletions(-) create mode 100644 docker-compose-1.yml diff --git a/docker-compose-1.yml b/docker-compose-1.yml new file mode 100644 index 000000000..39fc944ce --- /dev/null +++ b/docker-compose-1.yml @@ -0,0 +1,298 @@ +#fixme Clone openIM Server project before using docker-compose,project address:https://github.com/OpenIMSDK/Open-IM-Server.git +# The command that triggers this file to pull the image is "docker compose up -f" +version: '3' + +networks: + server: + driver: bridge + ipam: + driver: default + config: + - subnet: '${DOCKER_BRIDGE_SUBNET:-172.28.0.0/16}' + gateway: '${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}' + +services: + mongodb: + image: mongo:${MONGODB_IMAGE_VERSION-6.0.2} + ports: + - "${MONGO_PORT:-37017}:27017" + container_name: mongo + command: ["/bin/bash", "-c", "/docker-entrypoint-initdb.d/mongo-init.sh || true; docker-entrypoint.sh mongod --wiredTigerCacheSizeGB 1 --auth"] + volumes: + - "${DATA_DIR:-./}/components/mongodb/data/db:/data/db" + - "${DATA_DIR:-./}/components/mongodb/data/logs:/data/logs" + - "${DATA_DIR:-./}/components/mongodb/data/conf:/etc/mongo" + - "./scripts/mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro" + environment: + - TZ=Asia/Shanghai + - wiredTigerCacheSizeGB=1 + - MONGO_INITDB_ROOT_USERNAME=${MONGO_USERNAME:-root} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_PASSWORD:-openIM123} + - MONGO_INITDB_DATABASE=${MONGO_DATABASE:-openim_v3} + - MONGO_OPENIM_USERNAME=${MONGO_OPENIM_USERNAME:-openIM} # Non-root username + - MONGO_OPENIM_PASSWORD=${MONGO_OPENIM_PASSWORD:-openIM123456} # Non-root password + restart: always + networks: + server: + ipv4_address: ${MONGO_NETWORK_ADDRESS:-172.28.0.2} + + redis: + image: redis:${REDIS_IMAGE_VERSION:-7.0.0} + container_name: redis + ports: + - "${REDIS_PORT:-16379}:6379" + volumes: + - "${DATA_DIR:-./}/components/redis/data:/data" + - "${DATA_DIR:-./}/components/redis/config/redis.conf:/usr/local/redis/config/redis.conf" + environment: + TZ: Asia/Shanghai + restart: always + sysctls: + net.core.somaxconn: 1024 + command: redis-server --requirepass ${REDIS_PASSWORD:-openIM123} --appendonly yes + networks: + server: + ipv4_address: ${REDIS_NETWORK_ADDRESS:-172.28.0.3} + + zookeeper: + image: bitnami/zookeeper:${ZOOKEEPER_IMAGE_VERSION:-3.8} + container_name: zookeeper + ports: + - "${ZOOKEEPER_PORT:-12181}:2181" + volumes: + - "/etc/localtime:/etc/localtime" + environment: + - ALLOW_ANONYMOUS_LOGIN=yes + - TZ="Asia/Shanghai" + restart: always + networks: + server: + ipv4_address: ${ZOOKEEPER_NETWORK_ADDRESS:-172.28.0.5} + + kafka: + image: 'bitnami/kafka:${KAFKA_IMAGE_VERSION:-3.5.1}' + container_name: kafka + restart: always + user: ${KAFKA_USER:-root} + ports: + - "${KAFKA_PORT:-19094}:9094" + volumes: + - ./scripts/create-topic.sh:/opt/bitnami/kafka/create-topic.sh + - "${DATA_DIR:-./}/components/kafka:/bitnami/kafka" + command: > + bash -c "/opt/bitnami/scripts/kafka/run.sh & sleep 5; /opt/bitnami/kafka/create-topic.sh; wait" + environment: + - TZ=Asia/Shanghai + - KAFKA_CFG_NODE_ID=0 + - KAFKA_CFG_PROCESS_ROLES=controller,broker + - KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@:9093 + - KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093,EXTERNAL://:9094 + - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://${DOCKER_BRIDGE_GATEWAY:-172.28.0.1}:${KAFKA_PORT:-19094} + # - KAFKA_CFG_ADVERTISED_LISTENERS=PLAINTEXT://kafka:9092,EXTERNAL://127.0.0.1:${KAFKA_PORT:-19094} # Mac Deployment + - KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,EXTERNAL:PLAINTEXT,PLAINTEXT:PLAINTEXT + - KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER + networks: + server: + ipv4_address: ${KAFKA_NETWORK_ADDRESS:-172.28.0.4} + + minio: + image: minio/minio:${MINIO_IMAGE_VERSION:-RELEASE.2024-01-11T07-46-16Z} + ports: + - "${MINIO_PORT:-10005}:9000" + - "9090:9090" + container_name: minio + volumes: + - "${DATA_DIR:-./}/components/mnt/data:/data" + - "${DATA_DIR:-./}/components/mnt/config:/root/.minio" + environment: + MINIO_ROOT_USER: "${MINIO_ACCESS_KEY:-root}" + MINIO_ROOT_PASSWORD: "${MINIO_SECRET_KEY:-openIM123}" + restart: always + command: minio server /data --console-address ':9090' + networks: + server: + ipv4_address: ${MINIO_NETWORK_ADDRESS:-172.28.0.6} + + openim-web: + image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-web:${OPENIM_WEB_IMAGE_VERSION:-v3.5.0-docker} + container_name: openim-web + platform: linux/amd64 + restart: always + ports: + - "${OPENIM_WEB_PORT:-11001}:80" + networks: + server: + ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7} + + openim-admin: + # https://github.com/openimsdk/open-im-server/issues/1662 + image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35} + container_name: openim-admin + platform: linux/amd64 + restart: always + ports: + - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80" + networks: + server: + ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS:-172.28.0.13} + + prometheus: + image: prom/prometheus + container_name: prometheus + hostname: prometheus + restart: always + volumes: + - "${DATA_DIR:-./}/config/instance-down-rules.yml:/etc/prometheus/instance-down-rules.yml" + - "${DATA_DIR:-./}/config/prometheus.yml:/etc/prometheus/prometheus.yml" + ports: + - "${PROMETHEUS_PORT:-19090}:9090" + networks: + server: + ipv4_address: ${PROMETHEUS_NETWORK_ADDRESS:-172.28.0.10} + + alertmanager: + image: prom/alertmanager + container_name: alertmanager + hostname: alertmanager + restart: always + volumes: + - ${DATA_DIR:-./}/config/alertmanager.yml:/etc/alertmanager/alertmanager.yml + - ${DATA_DIR:-./}/config/email.tmpl:/etc/alertmanager/email.tmpl + ports: + - "${ALERT_MANAGER_PORT:-19093}:9093" + networks: + server: + ipv4_address: ${ALERT_MANAGER_NETWORK_ADDRESS:-172.28.0.14} + + grafana: + image: grafana/grafana + container_name: grafana + hostname: grafana + user: root + restart: always + ports: + - "${GRAFANA_PORT:-13000}:3000" + volumes: + - "${DATA_DIR:-./}/components/grafana:/var/lib/grafana" + networks: + server: + ipv4_address: ${GRAFANA_NETWORK_ADDRESS:-172.28.0.11} + + node-exporter: + image: quay.io/prometheus/node-exporter + container_name: node-exporter + hostname: node-exporter + restart: always + ports: + - "${NODE_EXPORTER_PORT:-19100}:9100" + networks: + server: + ipv4_address: ${NODE_EXPORTER_NETWORK_ADDRESS:-172.28.0.12} + +### Source code deployment does not require pulling the following mirrors + + # openim-server: + # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-server:${SERVER_IMAGE_VERSION:-main} + # container_name: openim-server + # ports: + # - "${OPENIM_WS_PORT:-10001}:${OPENIM_WS_PORT:-10001}" + # - "${API_OPENIM_PORT:-10002}:${API_OPENIM_PORT:-10002}" + # - "${API_PROM_PORT:-20100}:${API_PROM_PORT:-20100}" + # - "${USER_PROM_PORT:-20110}:${USER_PROM_PORT:-20110}" + # - "${FRIEND_PROM_PORT:-20120}:${FRIEND_PROM_PORT:-20120}" + # - "${MESSAGE_PROM_PORT:-20130}:${MESSAGE_PROM_PORT:-20130}" + # - "${MSG_GATEWAY_PROM_PORT:-20140}:${MSG_GATEWAY_PROM_PORT:-20140}" + # - "${GROUP_PROM_PORT:-20150}:${GROUP_PROM_PORT:-20150}" + # - "${AUTH_PROM_PORT:-20160}:${AUTH_PROM_PORT:-20160}" + # - "${PUSH_PROM_PORT:-20170}:${PUSH_PROM_PORT:-20170}" + # - "${CONVERSATION_PROM_PORT:-20230}:${CONVERSATION_PROM_PORT:-20230}" + # - "${RTC_PROM_PORT:-21300}:${RTC_PROM_PORT:-21300}" + # - "${THIRD_PROM_PORT:-21301}:${THIRD_PROM_PORT:-21301}" + # - "21400-21403:21400-21403" + # healthcheck: + # test: ["CMD", "/openim/openim-server/scripts/check-all.sh"] + # interval: 120s + # timeout: 30s + # retries: 5 + # env_file: + # - .env + # environment: + # - OPENIM_IP=${OPENIM_IP:-127.0.0.1} + # volumes: + # - "${DATA_DIR:-./}/openim-server/logs:/openim/openim-server/logs" + # - "${DATA_DIR:-./}/openim-server/_output/logs:/openim/openim-server/_output/logs" + # - "${DATA_DIR:-./}/openim-server/config:/openim/openim-server/config" + # restart: always + # depends_on: + # - kafka + # - mysql + # - mongodb + # - redis + # - minio + # logging: + # driver: json-file + # options: + # max-size: "1g" + # max-file: "2" + # networks: + # server: + # ipv4_address: ${OPENIM_SERVER_NETWORK_ADDRESS:-172.28.0.8} + + ### TODO: mysql is required to deploy the openim-chat component + # mysql: + # image: mysql:${MYSQL_IMAGE_VERSION:-5.7} + # platform: linux/amd64 + # ports: + # - "${MYSQL_PORT:-13306}:3306" + # container_name: mysql + # volumes: + # - "${DATA_DIR:-./}/components/mysql/data:/var/lib/mysql" + # - "/etc/localtime:/etc/localtime" + # environment: + # MYSQL_ROOT_PASSWORD: "${MYSQL_PASSWORD:-openIM123}" + # restart: always + # networks: + # server: + # ipv4_address: ${MYSQL_NETWORK_ADDRESS:-172.28.0.15} + + # openim-chat: + # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-chat:${CHAT_IMAGE_VERSION:-main} + # container_name: openim-chat + # healthcheck: + # test: ["CMD", "/openim/openim-chat/scripts/check_all.sh"] + # interval: 60s + # timeout: 30s + # retries: 5 + # env_file: + # - .env + # environment: + # - ZOOKEEPER_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1} + # - ZOOKEEPER_PORT=${ZOOKEEPER_PORT:-12181} + # - OPENIM_SERVER_ADDRESS=http://${OPENIM_SERVER_ADDRESS:-172.28.0.1} + # - API_OPENIM_PORT=${API_OPENIM_PORT:-10002} + # - MYSQL_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1} + # - MYSQL_PORT=${MYSQL_PORT:-13306} + # - REDIS_ADDRESS=${DOCKER_BRIDGE_GATEWAY:-172.28.0.1} + # - REDIS_PORT=${REDIS_PORT:-16379} + # ports: + # - "${OPENIM_CHAT_API_PORT:-10008}:10008" + # - "${OPENIM_ADMIN_API_PORT:-10009}:10009" + # volumes: + # - "${DATA_DIR:-./}/components/openim-chat/logs:/openim/openim-chat/logs" + # - "${DATA_DIR:-./}/components/openim-chat/_output/logs:/openim/openim-chat/_output/logs" + # - "${DATA_DIR:-./}/components/openim-chat/config:/openim/openim-chat/config" + # restart: always + # # user: root:root + # depends_on: + # - mysql + # - kafka + # - redis + # - zookeeper + # logging: + # driver: json-file + # options: + # max-size: "1g" + # max-file: "2" + # networks: + # server: + # ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9} diff --git a/docker-compose.yml b/docker-compose.yml index dcf7518e2..8538eec83 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,5 @@ #fixme Clone openIM Server project before using docker-compose,project address:https://github.com/OpenIMSDK/Open-IM-Server.git +# The command that triggers this file to pull the image is "docker compose up -d". version: '3' networks: @@ -123,6 +124,18 @@ services: server: ipv4_address: ${OPENIM_WEB_NETWORK_ADDRESS:-172.28.0.7} + openim-admin: + # https://github.com/openimsdk/open-im-server/issues/1662 + image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35} + container_name: openim-admin + platform: linux/amd64 + restart: always + ports: + - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80" + networks: + server: + ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS:-172.28.0.13} + ### TODO: Uncomment, or deploy using openim docker: https://github.com/openimsdk/openim-docker ### Uncomment and configure the following services as needed @@ -232,18 +245,6 @@ services: # server: # ipv4_address: ${OPENIM_CHAT_NETWORK_ADDRESS:-172.28.0.9} - # openim-admin: - # # https://github.com/openimsdk/open-im-server/issues/1662 - # image: ${IMAGE_REGISTRY:-ghcr.io/openimsdk}/openim-admin:${ADMIN_FRONT_VERSION:-toc-base-open-docker.35} - # container_name: openim-admin - # platform: linux/amd64 - # restart: always - # ports: - # - "${OPENIM_ADMIN_FRONT_PORT:-11002}:80" - # networks: - # server: - # ipv4_address: ${OPENIM_ADMIN_FRONT_NETWORK_ADDRESS:-172.28.0.13} - # prometheus: # image: prom/prometheus # container_name: prometheus diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 8558a23ea..903ecbb18 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -50,7 +50,6 @@ type conversationServer struct { conversationDatabase controller.ConversationDatabase conversationNotificationSender *notification.ConversationNotificationSender } - func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq, diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index 97bb03391..b951bf219 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -48,8 +48,7 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { } func IsAppManagerUid(ctx context.Context) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || - utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) + return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) } func CheckAdmin(ctx context.Context) error { From bd354f9a629fc38a18277f78a7c879a49fa2e55e Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:43:23 +0800 Subject: [PATCH 26/74] docs: add README-ES (#1896) Signed-off-by: kubbot & kubecub <3293172751ysy@gmail.com> Co-authored-by: kubbot & kubecub <3293172751ysy@gmail.com> --- docs/readme/README-ES.md | 193 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 docs/readme/README-ES.md diff --git a/docs/readme/README-ES.md b/docs/readme/README-ES.md new file mode 100644 index 000000000..f058617a0 --- /dev/null +++ b/docs/readme/README-ES.md @@ -0,0 +1,193 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ Acerca de OpenIM + +OpenIM es una plataforma de servicio diseñada específicamente para integrar chat, llamadas de audio y video, notificaciones y chatbots de IA en aplicaciones. Proporciona una gama de potentes API y Webhooks, lo que permite a los desarrolladores incorporar fácilmente estas características interactivas en sus aplicaciones. OpenIM no es una aplicación de chat independiente, sino que sirve como una plataforma para apoyar a otras aplicaciones en lograr funcionalidades de comunicación enriquecidas. El siguiente diagrama ilustra la interacción entre AppServer, AppClient, OpenIMServer y OpenIMSDK para explicar en detalle. + +![Relación App-OpenIM](./docs/images/oepnim-design.png) + +## 🚀 Acerca de OpenIMSDK + +**OpenIMSDK** es un SDK de mensajería instantánea diseñado para **OpenIMServer**, creado específicamente para su incorporación en aplicaciones cliente. Sus principales características y módulos son los siguientes: + ++ 🌟 Características Principales: + + - 📦 Almacenamiento local + - 🔔 Callbacks de escuchas + - 🛡️ Envoltura de API + - 🌐 Gestión de conexiones + ++ 📚 Módulos Principales: + + 1. 🚀 Inicialización y acceso + 2. 👤 Gestión de usuarios + 3. 👫 Gestión de amigos + 4. 🤖 Funciones de grupo + 5. 💬 Manejo de conversaciones + +Está construido con Golang y soporta despliegue multiplataforma, asegurando una experiencia de acceso consistente en todas las plataformas. + +👉 **[Explora el SDK de GO](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Acerca de OpenIMServer + ++ **OpenIMServer** tiene las siguientes características: + - 🌐 Arquitectura de microservicios: Soporta modo cluster, incluyendo un gateway y múltiples servicios rpc. + - 🚀 Métodos de despliegue diversos: Soporta el despliegue a través de código fuente, Kubernetes o Docker. + - Soporte para una base de usuarios masiva: Grupos super grandes con cientos de miles de usuarios, decenas de millones de usuarios y miles de millones de mensajes. + + + +### Funcionalidad Empresarial Mejorada: + ++ **API REST**: OpenIMServer ofrece APIs REST para sistemas empresariales, destinadas a empoderar a las empresas con más funcionalidades, como la creación de grupos y el envío de mensajes push a través de interfaces de backend. ++ **Webhooks**: OpenIMServer proporciona capacidades de callback para extender más formas de negocio. Un callback significa que OpenIMServer envía una solicitud al servidor empresarial antes o después de un cierto evento, como callbacks antes o después de enviar un mensaje. + +👉 **[Aprende más](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Arquitectura General + +Adéntrate en el corazón de la funcionalidad de Open-IM-Server con nuestro diagrama de arquitectura. + +![Arquitectura General](./docs/images/architecture-layers.png) + + +## :rocket: Inicio Rápido + + +:rocket: Inicio Rápido +Apoyamos muchas plataformas. Aquí están las direcciones para una experiencia rápida en el lado web: + +👉 **[ Demostración web en línea de OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 Para facilitar la experiencia del usuario, ofrecemos varias soluciones de despliegue. Puedes elegir tu método de despliegue de la lista a continuación: + ++ **[Guía de Despliegue de Código Fuente](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Guía de Despliegue con Docker](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Guía de Despliegue con Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Guía de Despliegue para Desarrolladores en Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Para Comenzar a Desarrollar en OpenIM + +[![Abrir en Contenedor de Desarrollo](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +Nuestro objetivo en OpenIM es construir una comunidad de código abierto de nivel superior. Tenemos un conjunto de estándares, +en el [repositorio de la Comunidad.](https://github.com/OpenIMSDK/community). + +Si te gustaría contribuir a este repositorio de Open-IM-Server, por favor lee nuestra [documentación para colaboradores](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + + +Antes de comenzar, asegúrate de que tus cambios sean demandados. Lo mejor para eso es crear una [nueva discusión](https://github.com/openimsdk/open-im-server/discussions/new/choose) O [Comunicación en Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), o si encuentras un problema, [repórtalo](https://github.com/openimsdk/open-im-server/issues/new/choose) primero. + +- [Referencia de API de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Registro de Bash de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Acciones de CI/CD de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Convenciones de Código de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Guías de Commit de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Guía de Desarrollo de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Estructura de Directorios de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Configuración de Entorno de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Referencia de Códigos de Error de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Flujo de Trabajo de Git de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [Guía de Cherry Pick de Git de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Flujo de Trabajo de GitHub de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [Estándares de Código Go de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Guías de Imágenes de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Configuración Inicial de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Guía de Instalación de Docker de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [Instalación del Sistema Linux de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [Guía de Desarrollo Linux de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Guía de Acciones Locales de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Convenciones de Registro de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Despliegue sin Conexión de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Herramientas Protoc de OpenIMM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Guía de Pruebas de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [Utilidades Go de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [Utilidades de Makefile de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [Utilidades de Script de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [Versionado de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Gestión de backend y despliegue de monitoreo](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Guía de Despliegue para Desarrolladores Mac de OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Comunidad + ++ 📚 [Comunidad de OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [Grupo de Interés de OpenIM](https://github.com/Openim-sigs) ++ 🚀 [Únete a nuestra comunidad de Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Únete a nuestro wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Reuniones de la Comunidad + +Queremos que cualquiera se involucre en nuestra comunidad y contribuya con código, ofrecemos regalos y recompensas, y te damos la bienvenida para que te unas a nosotros cada jueves por la noche. + +Nuestra conferencia está en [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, luego puedes buscar el pipeline de Open-IM-Server para unirte + +Tomamos notas de cada [reunión quincenal](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) en [discusiones de GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Nuestras notas de reuniones históricas, así como las repeticiones de las reuniones están disponibles en [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Quiénes Están Usando OpenIM + +Consulta nuestros [estudios de caso de usuarios](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) página para obtener una lista de los usuarios del proyecto. No dudes en dejar un [📝comentario](https://github.com/openimsdk/open-im-server/issues/379) y compartir tu caso de uso. +## :page_facing_up: Licencia + + +OpenIM está bajo la licencia Apache 2.0. Consulta [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) para ver el texto completo de la licencia. + + +El logotipo de OpenIM, incluyendo sus variaciones y versiones animadas, que se muestran en este repositorio [OpenIM](https://github.com/openimsdk/open-im-server) en los directorios [assets/logo](./assets/logo) y [assets/logo-gif](assets/logo-gif) están protegidos por las leyes de derechos de autor. +## 🔮 iGracias a nuestros colaboradores! + + + + From c63ea386afbcb6753f301b773e0c8b00adee2df4 Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Sun, 18 Feb 2024 18:44:49 +0800 Subject: [PATCH 27/74] docs: add README.md -FR (#1901) * docs: add README-FR Signed-off-by: Your Name <2631918708@qq.com> * docs: add README-FR Signed-off-by: longyuqing112 <2631918708@qq.com> --------- Signed-off-by: Your Name <2631918708@qq.com> Signed-off-by: longyuqing112 <2631918708@qq.com> --- docs/readme/README-FR.md | 178 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 docs/readme/README-FR.md diff --git a/docs/readme/README-FR.md b/docs/readme/README-FR.md new file mode 100644 index 000000000..5b72fc2b8 --- /dev/null +++ b/docs/readme/README-FR.md @@ -0,0 +1,178 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ + +## Ⓜ️ À propos de OpenIM + +OpenIM est une plateforme de services conçue spécifiquement pour intégrer des fonctionnalités de communication telles que le chat, les appels audio et vidéo, les notifications, ainsi que les robots de chat IA dans les applications. Elle offre une série d'API puissantes et de Webhooks, permettant aux développeurs d'incorporer facilement ces caractéristiques interactives dans leurs applications. OpenIM n'est pas en soi une application de chat autonome, mais sert de plateforme supportant d'autres applications pour réaliser des fonctionnalités de communication enrichies. L'image ci-dessous montre les relations d'interaction entre AppServer, AppClient, OpenIMServer et OpenIMSDK pour illustrer spécifiquement. + + + +![Relation App-OpenIM](../images/oepnim-design.png) + +## 🚀 À propos de OpenIMSDK + +**OpenIMSDK** est un SDK IM conçu pour **OpenIMServer** spécialement créé pour être intégré dans les applications clientes. Ses principales fonctionnalités et modules comprennent : + ++ 🌟 Fonctionnalités clés : + + - 📦 Stockage local + - 🔔 Rappels de l'écouteur + - 🛡️ Encapsulation d'API + - 🌐 Gestion de la connexion + + ## 📚 Modules principaux : + + 1. 🚀 Initialisation et connexion + 2. 👤 Gestion des utilisateurs + 3. 👫 Gestion des amis + 4. 🤖 Fonctionnalités de groupe + 5. 💬 Traitement des conversations + +Il est construit avec Golang et supporte le déploiement multiplateforme, assurant une expérience d'accès cohérente sur toutes les plateformes。 + +👉 **[Explorer le SDK GO](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 À propos de OpenIMServer + ++ **OpenIMServer** présente les caractéristiques suivantes : + - 🌐 Architecture microservices : prend en charge le mode cluster, incluant le gateway (passerelle) et plusieurs services rpc。 + - 🚀 Divers modes de déploiement : supporte le déploiement via le code source, Kubernetes ou Docker。 + - Support d'une masse d'utilisateurs : plus de cent mille pour les super grands groupes, des millions d'utilisateurs, et des milliards de messages。 + +### Fonctionnalités commerciales améliorées : + ++ **REST API**:OpenIMServer fournit une REST API pour les systèmes commerciaux, visant à accorder plus de fonctionnalités, telles que la création de groupes via l'interface backend, l'envoi de messages push, etc。 ++ **Webhooks**:OpenIMServer offre des capacités de rappel pour étendre davantage les formes d'entreprise. Un rappel signifie que OpenIMServer enverra une requête au serveur d'entreprise avant ou après qu'un événement se soit produit, comme un rappel avant ou après l'envoi d'un message。 + +👉 **[En savoir plus](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Architecture globale + +Plongez dans le cœur de la fonctionnalité d'Open-IM-Server avec notre diagramme d'architecture. + +![Architecture globale](../images/architecture-layers.png) + + +## :rocket: Démarrage rapide + +Nous prenons en charge de nombreuses plateformes. Voici les adresses pour une expérience rapide du côté web : + +👉 **[Démo web en ligne OpenIM](https://www.openim.io/zh/commercial)** + +🤲 Pour faciliter l'expérience utilisateur, nous proposons plusieurs solutions de déploiement. Vous pouvez choisir votre méthode de déploiement selon la liste ci-dessous : + ++ **[Guide de déploiement du code source](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Guide de déploiement Docker](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Guide de déploiement Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Guide de déploiement pour développeur Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Commencer à développer avec OpenIM + +Chez OpenIM, notre objectif est de construire une communauté open source de premier plan. Nous avons un ensemble de standards, disponibles dans le[ dépôt communautaire](https://github.com/OpenIMSDK/community)。 +Si vous souhaitez contribuer à ce dépôt Open-IM-Server, veuillez lire notre[ document pour les contributeurs](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md)。 + +Avant de commencer, assurez-vous que vos modifications sont nécessaires. La meilleure manière est de créer une[ nouvelle discussion ](https://github.com/openimsdk/open-im-server/discussions/new/choose) ou une [ communication Slack,](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q),ou si vous identifiez un problème, de[ signaler d'abord ](https://github.com/openimsdk/open-im-server/issues/new/choose)。 +- [Référence de l'API OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Journalisation Bash OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Actions CI/CD OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Conventions de code OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Directives de commit OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Guide de développement OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Structure de répertoire OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Configuration de l'environnement OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Référence des codes d'erreur OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Workflow Git OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [Guide Cherry Pick Git OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Workflow GitHub OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [Normes de code Go OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Directives d'image OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Configuration initiale OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Guide d'installation Docker OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [Installation du système Linux OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [Guide de développement Linux OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Guide des actions locales OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Conventions de journalisation OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Déploiement hors ligne OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Outils Protoc OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Guide de test OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [Utilitaire Go OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [Utilitaires Makefile OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [Utilitaires de script OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [Versionnement OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Gérer le déploiement du backend et la surveillance](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Guide de déploiement pour développeur Mac pour OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +>## :calendar: Réunions de la Communauté + +Nous voulons que tout le monde s'implique dans notre communauté et contribue au code, nous offrons des cadeaux et des récompenses, et nous vous invitons à nous rejoindre chaque jeudi soir. +Notre conférence se trouve dans le [ Slack OpenIM ](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, ensuite vous pouvez rechercher le pipeline Open-IM-Server pour rejoindre + +Nous prenons des notes de chaque [réunion bihebdomadaire ](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) dans les [discussions GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Nos notes de réunion historiques, ainsi que les rediffusions des réunions sont disponibles sur [ Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Qui Utilise OpenIM + +Consultez notre page [ études de cas d'utilisateurs ](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) pour une liste des utilisateurs du projet. N'hésitez pas à laisser un [📝commentaire](https://github.com/openimsdk/open-im-server/issues/379) et partager votre cas d'utilisation. + +## :page_facing_up: License + +OpenIM est sous licence Apache 2.0. Voir [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) pour le texte complet de la licence. + +Le logo OpenIM, y compris ses variations et versions animées, affiché dans ce dépôt[OpenIM](https://github.com/openimsdk/open-im-server) sous les répertoires [assets/logo](./assets/logo) et [assets/logo-gif](assets/logo-gif) sont protégés par les lois sur le droit d'auteur. + +## 🔮 Merci à nos contributeurs ! + + + + From 760fbc95e6e1587b3424596f0e9cd07eb6b0a474 Mon Sep 17 00:00:00 2001 From: Seal Bell Date: Sun, 18 Feb 2024 18:44:59 +0800 Subject: [PATCH 28/74] docs:add README_cs.md (#1917) * docs:add README_cs.md * Update .spelling_failures * Update .spelling_failures * Update .spelling_failures --------- Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- docs/readme/README_cs.md | 187 +++++++++++++++++++++++++++++++++++++ scripts/.spelling_failures | 25 ++++- 2 files changed, 211 insertions(+), 1 deletion(-) create mode 100644 docs/readme/README_cs.md diff --git a/docs/readme/README_cs.md b/docs/readme/README_cs.md new file mode 100644 index 000000000..5a9eeb232 --- /dev/null +++ b/docs/readme/README_cs.md @@ -0,0 +1,187 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ O OpenIM + +OpenIM je platforma služeb speciálně navržená pro integraci chatu, audio-video hovorů, upozornění a chatbotů AI do aplikací. Poskytuje řadu výkonných rozhraní API a webhooků, které vývojářům umožňují snadno začlenit tyto interaktivní funkce do svých aplikací. OpenIM není samostatná chatovací aplikace, ale spíše slouží jako platforma pro podporu jiných aplikací při dosahování bohatých komunikačních funkcí. Následující diagram ilustruje interakci mezi AppServer, AppClient, OpenIMServer a OpenIMSDK pro podrobné vysvětlení. + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 O OpenIMSDK + +**OpenIMSDK** je IM SDK navržený pro**OpenIMServer**, vytvořený speciálně pro vkládání do klientských aplikací. Jeho hlavní vlastnosti a moduly jsou následující: + ++ 🌟 Hlavní vlastnosti: + + - 📦 Místní úložiště + - 🔔 Zpětná volání posluchačů + - 🛡️ API obalování + - 🌐 Správa připojení + ++ 📚 hlavní moduly: + + 1. 🚀 Inicializace a přihlášení + 2. 👤 Správa uživatelů + 3. 👫 Správa přátel + 4. 🤖 Skupinové funkce + 5. 💬 Zpracování konverzace + +Je postaven pomocí Golang a podporuje nasazení napříč platformami, což zajišťuje konzistentní přístup na všech platformách. + +👉 **[Prozkoumat GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 O OpenIMServeru + ++ **OpenIMServer** má následující vlastnosti: + - 🌐 Architektura mikroslužeb: Podporuje režim clusteru, včetně brány a více služeb RPC. + - 🚀 Různé metody nasazení: Podporuje nasazení prostřednictvím zdrojového kódu, Kubernetes nebo Docker. + - Podpora masivní uživatelské základny: Super velké skupiny se stovkami tisíc uživatelů, desítkami milionů uživatelů a miliardami zpráv. + +### Vylepšené obchodní funkce: + ++ **REST API**: OpenIMServer nabízí REST API pro podnikové systémy, jejichž cílem je poskytnout podnikům více funkcí, jako je vytváření skupin a odesílání push zpráv přes backendová rozhraní. ++ **Webhooks**: OpenIMServer poskytuje možnosti zpětného volání pro rozšíření více obchodních formulářů. Zpětné volání znamená, že OpenIMServer odešle požadavek na obchodní server před nebo po určité události, jako jsou zpětná volání před nebo po odeslání zprávy. + +👉 **[Další informace](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Celková architektura + +Ponořte se do srdce funkčnosti Open-IM-Server s naším diagramem architektury. + +![Overall Architecture](../images/architecture-layers.png) + + +## :rocket: Rychlý start + +Podporujeme mnoho platforem. Zde jsou adresy pro rychlou práci na webové stránce: + +👉 **[Online webová ukázka OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 Pro usnadnění uživatelské zkušenosti nabízíme různá řešení nasazení. Způsob nasazení si můžete vybrat ze seznamu níže: + ++ **[Průvodce nasazením zdrojového kódu](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker Deployment Guide](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Průvodce nasazením Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Průvodce nasazením pro vývojáře Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Chcete-li začít vyvíjet OpenIM + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM Naším cílem je vybudovat špičkovou open source komunitu. Máme soubor standardů v [komunitním repozitáři](https://github.com/OpenIMSDK/community). + +Pokud byste chtěli přispět do tohoto úložiště Open-IM-Server, přečtěte si naši [dokumentaci pro přispěvatele](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + +Než začnete, ujistěte se, že jsou vaše změny vyžadovány. Nejlepší pro to je vytvořit [nová diskuze](https://github.com/openimsdk/open-im-server/discussions/new/choose) NEBO [Slack Communication](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), nebo pokud narazíte na problém, [nahlásit jej](https://github.com/openimsdk/open-im-server/issues/new/choose) jako první. + +- [OpenIM API Reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Protokolování OpenIM Bash](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Akce OpenIM CI/CD](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Konvence kódu OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Pokyny k zavázání OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Průvodce vývojem OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Struktura adresáře OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Nastavení prostředí OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Referenční kód chybového kódu OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Pracovní postup OpenIM Git](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git Cherry Pick Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Pracovní postup OpenIM GitHub](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [standardy kódu OpenIM Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Pokyny pro obrázky OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Počáteční konfigurace OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Průvodce instalací OpenIM Docker](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [nstalace systému OpenIM OpenIM Linux](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux Development Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Průvodce místními akcemi OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Konvence protokolování OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Offline nasazení OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Nástroje protokolu OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Příručka testování OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OpenIM Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM Versioning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Spravovat backend a monitorovat nasazení](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Průvodce nasazením pro vývojáře Mac pro OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Společenství + ++ 📚 [Komunita OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [Zájmová skupina OpenIM](https://github.com/Openim-sigs) ++ 🚀 [Připojte se k naší komunitě Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Připojte se k našemu wechatu](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Komunitní setkání + +Chceme, aby se do naší komunity a přispívání kódu zapojil kdokoli, nabízíme dárky a odměny a vítáme vás, abyste se k nám připojili každý čtvrtek večer. + +Naše konference je v [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, pak můžete vyhledat kanál Open-IM-Server a připojit se + +Zaznamenáváme si každou [dvoutýdenní schůzku](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting)do [diskuzí na GitHubu](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), naše historické poznámky ze schůzek a také záznamy schůzek jsou k dispozici na [Dokumenty Google :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Kdo používá OpenIM + +Podívejte se na naši stránku [případové studie uživatelů](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md), kde najdete seznam uživatelů projektu. Neváhejte zanechat[📝komentář](https://github.com/openimsdk/open-im-server/issues/379) a podělte se o svůj případ použití. + +## :page_facing_up: License + +OpenIM je licencován pod licencí Apache 2.0. Úplný text licence naleznete v [LICENCE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE). + +Logo OpenIM, včetně jeho variací a animovaných verzí, zobrazené v tomto úložišti [OpenIM](https://github.com/openimsdk/open-im-server)v adresářích [assets/logo](./assets/logo) a [assets/logo-gif](assets/logo-gif) je chráněno autorským právem. + +## 🔮 Děkujeme našim přispěvatelům! + + + + diff --git a/scripts/.spelling_failures b/scripts/.spelling_failures index 149d314ba..347246f2e 100644 --- a/scripts/.spelling_failures +++ b/scripts/.spelling_failures @@ -5,4 +5,27 @@ third_party/ translations/ logs .git -.golangci.yml \ No newline at end of file +.golangci.yml +docs/readme/README_uk.md +docs/readme/README_cs.md +docs/readme/README_hu.md +docs/readme/README_es.md +docs/readme/README_fa.md +docs/readme/README_fr.md +docs/readme/README_de.md +docs/readme/README_pl.md +docs/readme/README_id.md +docs/readme/README_fi.md +docs/readme/README_ml.md +docs/readme/README_ja.md +docs/readme/README_nl.md +docs/readme/README_it.md +docs/readme/README_ru.md +docs/readme/README_pt_BR +docs/readme/README_eo.md +docs/readme/README_ko.md +docs/readme/README_ar.md +docs/readme/README_vi.md +docs/readme/README_da.md +docs/readme/README_el.md +docs/readme/README_tr.md From 79217972944ed5d24e5c60af1da4182c921ee974 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Sun, 18 Feb 2024 19:09:49 +0800 Subject: [PATCH 29/74] Update and rename README-ES.md to README-e.md (#1918) * Update and rename README-ES.md to README-e.md * Rename README-e.md to README-es.md * Update and rename README-FR.md to README-fr.md * Update README-fr.md * Rename README-es.md to README_es.md * Rename README-fr.md to README_fr.md --- docs/readme/{README-ES.md => README_es.md} | 58 +++++++++++----------- docs/readme/{README-FR.md => README_fr.md} | 56 ++++++++++----------- 2 files changed, 57 insertions(+), 57 deletions(-) rename docs/readme/{README-ES.md => README_es.md} (89%) rename docs/readme/{README-FR.md => README_fr.md} (88%) diff --git a/docs/readme/README-ES.md b/docs/readme/README_es.md similarity index 89% rename from docs/readme/README-ES.md rename to docs/readme/README_es.md index f058617a0..cd1b7290e 100644 --- a/docs/readme/README-ES.md +++ b/docs/readme/README_es.md @@ -1,6 +1,6 @@

- +

@@ -19,31 +19,31 @@

- Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

@@ -55,7 +55,7 @@ OpenIM es una plataforma de servicio diseñada específicamente para integrar chat, llamadas de audio y video, notificaciones y chatbots de IA en aplicaciones. Proporciona una gama de potentes API y Webhooks, lo que permite a los desarrolladores incorporar fácilmente estas características interactivas en sus aplicaciones. OpenIM no es una aplicación de chat independiente, sino que sirve como una plataforma para apoyar a otras aplicaciones en lograr funcionalidades de comunicación enriquecidas. El siguiente diagrama ilustra la interacción entre AppServer, AppClient, OpenIMServer y OpenIMSDK para explicar en detalle. -![Relación App-OpenIM](./docs/images/oepnim-design.png) +![Relación App-OpenIM](../../docs/images/oepnim-design.png) ## 🚀 Acerca de OpenIMSDK @@ -100,7 +100,7 @@ Está construido con Golang y soporta despliegue multiplataforma, asegurando una Adéntrate en el corazón de la funcionalidad de Open-IM-Server con nuestro diagrama de arquitectura. -![Arquitectura General](./docs/images/architecture-layers.png) +![Arquitectura General](../../docs/images/architecture-layers.png) ## :rocket: Inicio Rápido @@ -185,7 +185,7 @@ Consulta nuestros [estudios de caso de usuarios](https://github.com/OpenIMSDK/co OpenIM está bajo la licencia Apache 2.0. Consulta [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) para ver el texto completo de la licencia. -El logotipo de OpenIM, incluyendo sus variaciones y versiones animadas, que se muestran en este repositorio [OpenIM](https://github.com/openimsdk/open-im-server) en los directorios [assets/logo](./assets/logo) y [assets/logo-gif](assets/logo-gif) están protegidos por las leyes de derechos de autor. +El logotipo de OpenIM, incluyendo sus variaciones y versiones animadas, que se muestran en este repositorio [OpenIM](https://github.com/openimsdk/open-im-server) en los directorios [assets/logo](../../assets/logo) y [assets/logo-gif](assets/logo-gif) están protegidos por las leyes de derechos de autor. ## 🔮 iGracias a nuestros colaboradores! diff --git a/docs/readme/README-FR.md b/docs/readme/README_fr.md similarity index 88% rename from docs/readme/README-FR.md rename to docs/readme/README_fr.md index 5b72fc2b8..e707fc59b 100644 --- a/docs/readme/README-FR.md +++ b/docs/readme/README_fr.md @@ -1,6 +1,6 @@

- +

@@ -20,30 +20,30 @@

Englist · - 中文 · - Українська · - Česky · - Magyar · - Español · - فارسی · - Français · - Deutsch · - Polski · - Indonesian · - Suomi · - മലയാളം · - 日本語 · - Nederlands · - Italiano · - Русский · - Português (Brasil) · - Esperanto · - 한국어 · - العربي · - Tiếng Việt · - Dansk · - Ελληνικά · - Türkçe + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe

@@ -58,7 +58,7 @@ OpenIM est une plateforme de services conçue spécifiquement pour intégrer des -![Relation App-OpenIM](../images/oepnim-design.png) +![Relation App-OpenIM](../../images/oepnim-design.png) ## 🚀 À propos de OpenIMSDK @@ -101,7 +101,7 @@ Il est construit avec Golang et supporte le déploiement multiplateforme, assura Plongez dans le cœur de la fonctionnalité d'Open-IM-Server avec notre diagramme d'architecture. -![Architecture globale](../images/architecture-layers.png) +![Architecture globale](../../images/architecture-layers.png) ## :rocket: Démarrage rapide @@ -169,7 +169,7 @@ Consultez notre page [ études de cas d'utilisateurs ](https://github.com/OpenIM OpenIM est sous licence Apache 2.0. Voir [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) pour le texte complet de la licence. -Le logo OpenIM, y compris ses variations et versions animées, affiché dans ce dépôt[OpenIM](https://github.com/openimsdk/open-im-server) sous les répertoires [assets/logo](./assets/logo) et [assets/logo-gif](assets/logo-gif) sont protégés par les lois sur le droit d'auteur. +Le logo OpenIM, y compris ses variations et versions animées, affiché dans ce dépôt[OpenIM](https://github.com/openimsdk/open-im-server) sous les répertoires [assets/logo](../../assets/logo) et [assets/logo-gif](assets/logo-gif) sont protégés par les lois sur le droit d'auteur. ## 🔮 Merci à nos contributeurs ! From cbce4dae879b61ac0f1a23384ce3826f1c09463a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 19:19:54 +0800 Subject: [PATCH 30/74] feat(deps): bump the gomod-deps group with 17 updates (#1913) Bumps the gomod-deps group with 17 updates: | Package | From | To | | --- | --- | --- | | [github.com/OpenIMSDK/tools](https://github.com/OpenIMSDK/tools) | `0.0.33` | `0.0.35` | | [github.com/go-playground/validator/v10](https://github.com/go-playground/validator) | `10.15.5` | `10.18.0` | | [github.com/gorilla/websocket](https://github.com/gorilla/websocket) | `1.5.0` | `1.5.1` | | [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) | `7.0.63` | `7.0.67` | | [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | `1.17.0` | `1.18.0` | | [go.mongodb.org/mongo-driver](https://github.com/mongodb/mongo-go-driver) | `1.12.1` | `1.14.0` | | [golang.org/x/image](https://github.com/golang/image) | `0.13.0` | `0.15.0` | | [google.golang.org/api](https://github.com/googleapis/google-api-go-client) | `0.148.0` | `0.165.0` | | [google.golang.org/grpc](https://github.com/grpc/grpc-go) | `1.59.0` | `1.61.0` | | google.golang.org/protobuf | `1.31.0` | `1.32.0` | | [github.com/google/uuid](https://github.com/google/uuid) | `1.5.0` | `1.6.0` | | [github.com/IBM/sarama](https://github.com/IBM/sarama) | `1.41.3` | `1.42.2` | | [github.com/aliyun/aliyun-oss-go-sdk](https://github.com/aliyun/aliyun-oss-go-sdk) | `2.2.9+incompatible` | `3.0.2+incompatible` | | [github.com/redis/go-redis/v9](https://github.com/redis/go-redis) | `9.2.1` | `9.4.0` | | [github.com/tencentyun/cos-go-sdk-v5](https://github.com/tencentyun/cos-go-sdk-v5) | `0.7.45` | `0.7.46` | | [golang.org/x/sync](https://github.com/golang/sync) | `0.4.0` | `0.6.0` | | [github.com/spf13/cobra](https://github.com/spf13/cobra) | `1.7.0` | `1.8.0` | Updates `github.com/OpenIMSDK/tools` from 0.0.33 to 0.0.35 - [Release notes](https://github.com/OpenIMSDK/tools/releases) - [Commits](https://github.com/OpenIMSDK/tools/compare/v0.0.33...v0.0.35) Updates `github.com/go-playground/validator/v10` from 10.15.5 to 10.18.0 - [Release notes](https://github.com/go-playground/validator/releases) - [Commits](https://github.com/go-playground/validator/compare/v10.15.5...v10.18.0) Updates `github.com/gorilla/websocket` from 1.5.0 to 1.5.1 - [Release notes](https://github.com/gorilla/websocket/releases) - [Commits](https://github.com/gorilla/websocket/compare/v1.5.0...v1.5.1) Updates `github.com/minio/minio-go/v7` from 7.0.63 to 7.0.67 - [Release notes](https://github.com/minio/minio-go/releases) - [Commits](https://github.com/minio/minio-go/compare/v7.0.63...v7.0.67) Updates `github.com/prometheus/client_golang` from 1.17.0 to 1.18.0 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.17.0...v1.18.0) Updates `go.mongodb.org/mongo-driver` from 1.12.1 to 1.14.0 - [Release notes](https://github.com/mongodb/mongo-go-driver/releases) - [Commits](https://github.com/mongodb/mongo-go-driver/compare/v1.12.1...v1.14.0) Updates `golang.org/x/image` from 0.13.0 to 0.15.0 - [Commits](https://github.com/golang/image/compare/v0.13.0...v0.15.0) Updates `google.golang.org/api` from 0.148.0 to 0.165.0 - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.148.0...v0.165.0) Updates `google.golang.org/grpc` from 1.59.0 to 1.61.0 - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.59.0...v1.61.0) Updates `google.golang.org/protobuf` from 1.31.0 to 1.32.0 Updates `github.com/google/uuid` from 1.5.0 to 1.6.0 - [Release notes](https://github.com/google/uuid/releases) - [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/uuid/compare/v1.5.0...v1.6.0) Updates `github.com/IBM/sarama` from 1.41.3 to 1.42.2 - [Release notes](https://github.com/IBM/sarama/releases) - [Changelog](https://github.com/IBM/sarama/blob/main/CHANGELOG.md) - [Commits](https://github.com/IBM/sarama/compare/v1.41.3...v1.42.2) Updates `github.com/aliyun/aliyun-oss-go-sdk` from 2.2.9+incompatible to 3.0.2+incompatible - [Release notes](https://github.com/aliyun/aliyun-oss-go-sdk/releases) - [Changelog](https://github.com/aliyun/aliyun-oss-go-sdk/blob/master/CHANGELOG.md) - [Commits](https://github.com/aliyun/aliyun-oss-go-sdk/compare/v2.2.9...v3.0.2) Updates `github.com/redis/go-redis/v9` from 9.2.1 to 9.4.0 - [Release notes](https://github.com/redis/go-redis/releases) - [Changelog](https://github.com/redis/go-redis/blob/master/CHANGELOG.md) - [Commits](https://github.com/redis/go-redis/compare/v9.2.1...v9.4.0) Updates `github.com/tencentyun/cos-go-sdk-v5` from 0.7.45 to 0.7.46 - [Release notes](https://github.com/tencentyun/cos-go-sdk-v5/releases) - [Changelog](https://github.com/tencentyun/cos-go-sdk-v5/blob/master/CHANGELOG.md) - [Commits](https://github.com/tencentyun/cos-go-sdk-v5/compare/v0.7.45...v0.7.46) Updates `golang.org/x/sync` from 0.4.0 to 0.6.0 - [Commits](https://github.com/golang/sync/compare/v0.4.0...v0.6.0) Updates `github.com/spf13/cobra` from 1.7.0 to 1.8.0 - [Release notes](https://github.com/spf13/cobra/releases) - [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0) --- updated-dependencies: - dependency-name: github.com/OpenIMSDK/tools dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-deps - dependency-name: github.com/go-playground/validator/v10 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/gorilla/websocket dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-deps - dependency-name: github.com/minio/minio-go/v7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-deps - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: go.mongodb.org/mongo-driver dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: golang.org/x/image dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/google/uuid dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/IBM/sarama dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/aliyun/aliyun-oss-go-sdk dependency-type: direct:production update-type: version-update:semver-major dependency-group: gomod-deps - dependency-name: github.com/redis/go-redis/v9 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/tencentyun/cos-go-sdk-v5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: gomod-deps - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps - dependency-name: github.com/spf13/cobra dependency-type: direct:production update-type: version-update:semver-minor dependency-group: gomod-deps ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 89 ++++++++++++++------------ go.sum | 193 ++++++++++++++++++++++++++++++--------------------------- 2 files changed, 150 insertions(+), 132 deletions(-) diff --git a/go.mod b/go.mod index ab138e68c..be835c0a4 100644 --- a/go.mod +++ b/go.mod @@ -5,55 +5,55 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.55 - github.com/OpenIMSDK/tools v0.0.33 + github.com/OpenIMSDK/tools v0.0.35 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 - github.com/go-playground/validator/v10 v10.15.5 + github.com/go-playground/validator/v10 v10.18.0 github.com/gogo/protobuf v1.3.2 github.com/golang-jwt/jwt/v4 v4.5.0 - github.com/gorilla/websocket v1.5.0 + github.com/gorilla/websocket v1.5.1 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect - github.com/minio/minio-go/v7 v7.0.63 + github.com/minio/minio-go/v7 v7.0.67 github.com/mitchellh/mapstructure v1.5.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.17.0 + github.com/prometheus/client_golang v1.18.0 github.com/robfig/cron/v3 v3.0.1 github.com/sirupsen/logrus v1.9.3 // indirect github.com/stretchr/testify v1.8.4 - go.mongodb.org/mongo-driver v1.12.1 - golang.org/x/image v0.13.0 - google.golang.org/api v0.148.0 - google.golang.org/grpc v1.59.0 - google.golang.org/protobuf v1.31.0 + go.mongodb.org/mongo-driver v1.14.0 + golang.org/x/image v0.15.0 + google.golang.org/api v0.165.0 + google.golang.org/grpc v1.61.0 + google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 ) -require github.com/google/uuid v1.5.0 +require github.com/google/uuid v1.6.0 require ( - github.com/IBM/sarama v1.41.3 - github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible + github.com/IBM/sarama v1.42.2 + github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible github.com/go-redis/redis v6.15.9+incompatible - github.com/redis/go-redis/v9 v9.2.1 + github.com/redis/go-redis/v9 v9.4.0 github.com/spf13/pflag v1.0.5 github.com/stathat/consistent v1.0.0 - github.com/tencentyun/cos-go-sdk-v5 v0.7.45 - golang.org/x/sync v0.4.0 + github.com/tencentyun/cos-go-sdk-v5 v0.7.46 + golang.org/x/sync v0.6.0 gopkg.in/src-d/go-git.v4 v4.13.1 gotest.tools v2.2.0+incompatible ) require ( - cloud.google.com/go v0.110.8 // indirect - cloud.google.com/go/compute v1.23.0 // indirect + cloud.google.com/go v0.112.0 // indirect + cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/firestore v1.13.0 // indirect - cloud.google.com/go/iam v1.1.2 // indirect - cloud.google.com/go/longrunning v0.5.1 // indirect - cloud.google.com/go/storage v1.30.1 // indirect + cloud.google.com/go/firestore v1.14.0 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/longrunning v0.5.4 // indirect + cloud.google.com/go/storage v1.36.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -62,12 +62,15 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/eapache/go-resiliency v1.4.0 // indirect + github.com/eapache/go-resiliency v1.5.0 // indirect github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 // indirect github.com/eapache/queue v1.1.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gin-contrib/sse v0.1.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -76,7 +79,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/s2a-go v0.1.7 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.1 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -95,9 +98,9 @@ require ( github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/lithammer/shortuuid v3.0.0+incompatible // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -108,11 +111,11 @@ require ( github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/gomega v1.18.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pierrec/lz4/v4 v4.1.18 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect - github.com/prometheus/common v0.44.0 // indirect - github.com/prometheus/procfs v0.11.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect @@ -125,19 +128,23 @@ require ( github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect + go.opentelemetry.io/otel v1.23.0 // indirect + go.opentelemetry.io/otel/metric v1.23.0 // indirect + go.opentelemetry.io/otel/trace v1.23.0 // indirect go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/oauth2 v0.13.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/net v0.21.0 // indirect + golang.org/x/oauth2 v0.17.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gorm.io/gorm v1.25.4 // indirect @@ -149,9 +156,9 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/lestrrat-go/strftime v1.0.6 // indirect github.com/mattn/go-isatty v0.0.19 // indirect - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/ugorji/go/codec v1.2.11 // indirect go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.19.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index 94a516366..c4b661f1c 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,32 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= -cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= +cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= +cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/firestore v1.13.0 h1:/3S4RssUV4GO/kvgJZB+tayjhOfyAHs+KcpJgRVu/Qk= -cloud.google.com/go/firestore v1.13.0/go.mod h1:QojqqOh8IntInDUSTAh0c8ZsPYAr68Ma8c5DWOy8xb8= -cloud.google.com/go/iam v1.1.2 h1:gacbrBdWcoVmGLozRuStX45YKvJtzIjJdAolzUs1sm4= -cloud.google.com/go/iam v1.1.2/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= -cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= -cloud.google.com/go/longrunning v0.5.1/go.mod h1:spvimkwdz6SPWKEt/XBij79E9fiTkHSQl/fRUUQJYJc= -cloud.google.com/go/storage v1.30.1 h1:uOdMxAs8HExqBlnLtnQyP0YkvbiDpdGShGKtx6U/oNM= -cloud.google.com/go/storage v1.30.1/go.mod h1:NfxhC0UJE1aXSx7CIIbCf7y9HKT7BiccwkR7+P7gN8E= +cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw= +cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= +cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= +cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= +cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= +cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/IBM/sarama v1.41.3 h1:MWBEJ12vHC8coMjdEXFq/6ftO6DUZnQlFYcxtOJFa7c= -github.com/IBM/sarama v1.41.3/go.mod h1:Xxho9HkHd4K/MDUo/T/sOqwtX/17D33++E9Wib6hUdQ= +github.com/IBM/sarama v1.42.2 h1:VoY4hVIZ+WQJ8G9KNY/SQlWguBQXQ9uvFPOnrcu8hEw= +github.com/IBM/sarama v1.42.2/go.mod h1:FLPGUGwYqEs62hq2bVG6Io2+5n+pS6s/WOXVKWSLFtE= github.com/OpenIMSDK/protocol v0.0.55 h1:eBjg8DyuhxGmuCUjpoZjg6MJJJXU/xJ3xJwFhrn34yA= github.com/OpenIMSDK/protocol v0.0.55/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.33 h1:rvFCxXaXxLv1MJFC4qcoWRGwKBnV+hR68UN2N0/zZhE= -github.com/OpenIMSDK/tools v0.0.33/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= +github.com/OpenIMSDK/tools v0.0.35 h1:YH8UYoaErXqfNrwpUvQxe8nhL++gFH6qCisQPyzk0w8= +github.com/OpenIMSDK/tools v0.0.35/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible h1:Sg/2xHwDrioHpxTN6WMiwbXTpUEinBpHsN7mG21Rc2k= -github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g= +github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= @@ -54,7 +54,8 @@ github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cncf/xds/go v0.0.0-20231109132714-523115ebc101 h1:7To3pQ+pZo0i3dsWEbinPNFs5gPSBOsJtx3wTT94VBY= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -66,8 +67,8 @@ github.com/dtm-labs/rockscache v0.1.1 h1:6S1vgaHvGqrLd8Ka4hRTKeKPV7v+tT0MSkTIX81 github.com/dtm-labs/rockscache v0.1.1/go.mod h1:c76WX0kyIibmQ2ACxUXvDvaLykoPakivMqIxt+UzE7A= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eapache/go-resiliency v1.4.0 h1:3OK9bWpPk5q6pbFAaYSEwD9CLUSHG8bnZuqX2yMt3B0= -github.com/eapache/go-resiliency v1.4.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= +github.com/eapache/go-resiliency v1.5.0 h1:dRsaR00whmQD+SgVKlq/vCRFNgtEb5yppyeVos3Yce0= +github.com/eapache/go-resiliency v1.5.0/go.mod h1:5yPzW0MIvSe0JDsv0v+DvcjEv2FyD6iZYSs1ZI+iQho= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3 h1:Oy0F4ALJ04o5Qqpdz8XLIpNA3WM/iSIXqxtqo7UGVws= github.com/eapache/go-xerial-snappy v0.0.0-20230731223053-c322873962e3/go.mod h1:YvSRo5mw33fLEx1+DlK6L2VV43tJt5Eyel9n9XBcR+0= github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc= @@ -78,26 +79,34 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= +github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24= -github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= +github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= @@ -115,7 +124,6 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= @@ -129,7 +137,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -152,16 +159,16 @@ github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.1 h1:SBWmZhjUDRorQxrN0nwzf+AHBxnbFjViHQS4P0yVpmQ= -github.com/googleapis/enterprise-certificate-proxy v0.3.1/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= +github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= @@ -203,7 +210,6 @@ github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -216,8 +222,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= @@ -228,12 +234,12 @@ github.com/lithammer/shortuuid v3.0.0+incompatible h1:NcD0xWW/MZYXEHa6ITy6kaXN5n github.com/lithammer/shortuuid v3.0.0+incompatible/go.mod h1:FR74pbAuElzOUuenUHTK2Tciko1/vKuIKS9dSkDrA4w= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ= -github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4= +github.com/minio/minio-go/v7 v7.0.67 h1:BeBvZWAS+kRJm1vGTMJYVjKUNoo0FoEt/wUWdUtfmh8= +github.com/minio/minio-go/v7 v7.0.67/go.mod h1:+UXocnUeZ3wHvVh5s95gcrA4YjMIbccT6ubB+1m054A= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -270,26 +276,26 @@ github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5h github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= -github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= -github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= -github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= -github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= +github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= +github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= @@ -300,8 +306,8 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= @@ -320,14 +326,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/kms v1.0.563/go.mod h1:uom4Nvi9W+Qkom0exYiJ9VWJjXwyxtPYTkKkaLMlfE0= -github.com/tencentyun/cos-go-sdk-v5 v0.7.45 h1:5/ZGOv846tP6+2X7w//8QjLgH2KcUK+HciFbfjWquFU= -github.com/tencentyun/cos-go-sdk-v5 v0.7.45/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE= +github.com/tencentyun/cos-go-sdk-v5 v0.7.46 h1:IeTiMR8qZ7iQWhAGb1niw5vt0T1TfAwPeB8Gn/oTkuk= +github.com/tencentyun/cos-go-sdk-v5 v0.7.46/go.mod h1:DH9US8nB+AJXqwu/AMOrCFN1COv3dpytXuJWHgdg7kE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -345,10 +350,21 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mongodb.org/mongo-driver v1.12.1 h1:nLkghSU8fQNaK7oUmDhQFsnrtcoNy7Z6LVFKsEecqgE= -go.mongodb.org/mongo-driver v1.12.1/go.mod h1:/rGBTebI3XYboVmgz+Wv3Bcbl3aD0QF9zl6kDDw18rQ= +go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= +go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 h1:UNQQKPfTDe1J81ViolILjTKPr9WetKW6uei2hFgJmFs= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0/go.mod h1:r9vWsPS/3AQItv3OSlEJ/E4mbrhUbbw18meOjArPtKQ= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 h1:sv9kVfal0MK0wBMCOGr+HeJm9v803BkJxGrk2au7j08= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0/go.mod h1:SK2UL73Zy1quvRPonmOmRDiWk1KBV3LyIeeIxcEApWw= +go.opentelemetry.io/otel v1.23.0 h1:Df0pqjqExIywbMCMTxkAwzjLZtRf+bBKLbUcpxO2C9E= +go.opentelemetry.io/otel v1.23.0/go.mod h1:YCycw9ZeKhcJFrb34iVSkyT0iczq/zYDtZYFufObyB0= +go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV+9Cnpo= +go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI= +go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= @@ -365,13 +381,12 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/image v0.13.0 h1:3cge/F/QTkNLauhf2QoE9zp+7sr+ZcL4HnoZmdwg9sg= -golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk= +golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8= +golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -384,7 +399,6 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -394,24 +408,22 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ= +golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -434,12 +446,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -467,29 +479,28 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.148.0 h1:HBq4TZlN4/1pNcu0geJZ/Q50vIwIXT532UIMYoo0vOs= -google.golang.org/api v0.148.0/go.mod h1:8/TBgwaKjfqTdacOJrOv2+2Q6fBDU1uHKK06oGSkxzU= +google.golang.org/api v0.165.0 h1:zd5d4JIIIaYYsfVy1HzoXYZ9rWCSBxxAglbczzo7Bgc= +google.golang.org/api v0.165.0/go.mod h1:2OatzO7ZDQsoS7IFf3rvsE17/TldiU3F/zxFHeqUB5o= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= -google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= -google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a h1:a2MQQVoTo96JC9PMGtGBymLp7+/RzpFc2yX/9WfFg1c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe h1:USL2DhxfgRchafRvt/wYyyQNzwgL7ZiURcozOE/Pkvo= +google.golang.org/genproto v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac h1:OZkkudMUu9LVQMCoRUbI/1p5VCo9BOrlvkqMvWtqa6s= +google.golang.org/genproto/googleapis/api v0.0.0-20240116215550-a9fa1716bcac/go.mod h1:B5xPO//w8qmBDjGReYLpR6UJPnkldGkCSMoH/2vxJeg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014 h1:FSL3lRCkhaPFxqi0s9o+V4UI2WTzAVOvkgbd4kVV4Wg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240205150955-31a09d347014/go.mod h1:SaPjaZGWb0lPqs6Ittu0spdfrOArqji4ZdeP5IC/9N4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -501,8 +512,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From c754ec6e97e457540375109158bff93fc5a70f14 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Sun, 18 Feb 2024 20:16:47 +0800 Subject: [PATCH 31/74] fix: refactoring code of graceful exits (#1885) * fix: plant a layer * fix: print chanal * fix: print sigs * fix: print the sigs * fix: reconstruct exit gracefully * fix: fix the timeout * fix: fix the netDone * fix: fix the process exit * fix: refactor the elegant startup code * fix: fix the Signal.Notify * fix: fix the code * fix: remove not used header import. * Update init.go * fix: fix the InitConfig error * fix: fix branch name * fix: fix the signal value * fix: replace the signal with SIGTERM * fix: fix the script * fix: fix the unsolve error * fix: return the SIGTERM received,shutting down * fix: fix the tranfer exit error * fix: fix the error * fix: replace the SIGnal * fix: del the error return in tranfer * fix: fix SIGTERM error * fix: del the unreachalbe code * fix: fix the make stop print error --------- Co-authored-by: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Co-authored-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- cmd/openim-api/main.go | 44 ++++++--- cmd/openim-cmdutils/main.go | 7 +- cmd/openim-crontask/main.go | 7 +- cmd/openim-msggateway/main.go | 7 +- cmd/openim-msgtransfer/main.go | 7 +- cmd/openim-push/main.go | 7 +- cmd/openim-rpc/openim-rpc-auth/main.go | 8 +- .../openim-rpc-conversation/main.go | 7 +- cmd/openim-rpc/openim-rpc-friend/main.go | 7 +- cmd/openim-rpc/openim-rpc-group/main.go | 7 +- cmd/openim-rpc/openim-rpc-msg/main.go | 7 +- cmd/openim-rpc/openim-rpc-third/main.go | 7 +- cmd/openim-rpc/openim-rpc-user/main.go | 7 +- internal/msggateway/init.go | 21 +---- internal/msggateway/n_ws_server.go | 67 ++++++-------- internal/msgtransfer/init.go | 91 ++++++++----------- internal/push/callback.go | 2 +- internal/push/consumer_init.go | 10 +- internal/push/push_rpc_server.go | 23 ++--- internal/tools/cron_task.go | 2 +- pkg/common/ginprometheus/ginprometheus.go | 22 +++-- pkg/common/kafka/consumer_group.go | 31 ++----- pkg/common/startrpc/start.go | 88 +++++++++++------- pkg/util/genutil/genutil.go | 15 +++ scripts/githooks/pre-commit.sh | 2 +- scripts/install/openim-push.sh | 2 +- scripts/lib/util.sh | 4 +- 27 files changed, 241 insertions(+), 268 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index bbb5eb968..c8746bc20 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -17,6 +17,7 @@ package main import ( "context" "fmt" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net" "net/http" _ "net/http/pprof" @@ -46,8 +47,7 @@ func main() { apiCmd.AddPrometheusPortFlag() apiCmd.AddApi(run) if err := apiCmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } @@ -76,12 +76,21 @@ func run(port int, proPort int) error { if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.Config.EncodeConfig()); err != nil { return err } - + var ( + netDone = make(chan struct{}, 1) + netErr error + ) router := api.NewGinRouter(client, rdb) if config.Config.Prometheus.Enable { - p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api")) - p.SetListenAddress(fmt.Sprintf(":%d", proPort)) - p.Use(router) + go func() { + p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api")) + p.SetListenAddress(fmt.Sprintf(":%d", proPort)) + if err = p.Use(router); err != nil && err != http.ErrServerClosed { + netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", proPort)) + netDone <- struct{}{} + } + }() + } var address string @@ -92,24 +101,31 @@ func run(port int, proPort int) error { } server := http.Server{Addr: address, Handler: router} + go func() { err = server.ListenAndServe() if err != nil && err != http.ErrServerClosed { - os.Exit(1) + netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr)) + netDone <- struct{}{} + } }() sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - <-sigs + signal.Notify(sigs, syscall.SIGTERM) ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() - - // graceful shutdown operation. - if err := server.Shutdown(ctx); err != nil { - return err + select { + case <-sigs: + util.SIGUSR1Exit() + err := server.Shutdown(ctx) + if err != nil { + return errs.Wrap(err, "shutdown err") + } + case <-netDone: + close(netDone) + return netErr } - return nil } diff --git a/cmd/openim-cmdutils/main.go b/cmd/openim-cmdutils/main.go index 45b324766..f6b788933 100644 --- a/cmd/openim-cmdutils/main.go +++ b/cmd/openim-cmdutils/main.go @@ -15,10 +15,8 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -57,7 +55,6 @@ func main() { // openIM clear msg --clearAll msgUtilsCmd.AddCommand(&getCmd.Command, &fixCmd.Command, &clearCmd.Command) if err := msgUtilsCmd.Execute(); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go index 324001690..b284fd773 100644 --- a/cmd/openim-crontask/main.go +++ b/cmd/openim-crontask/main.go @@ -15,17 +15,14 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/tools" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { cronTaskCmd := cmd.NewCronTaskCmd() if err := cronTaskCmd.Exec(tools.StartTask); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go index 5339891c8..ed67b8f5d 100644 --- a/cmd/openim-msggateway/main.go +++ b/cmd/openim-msggateway/main.go @@ -15,10 +15,8 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -28,7 +26,6 @@ func main() { msgGatewayCmd.AddPrometheusPortFlag() if err := msgGatewayCmd.Exec(); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-msgtransfer/main.go b/cmd/openim-msgtransfer/main.go index cf1b44a55..84fbbd2ea 100644 --- a/cmd/openim-msgtransfer/main.go +++ b/cmd/openim-msgtransfer/main.go @@ -15,10 +15,8 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -26,7 +24,6 @@ func main() { msgTransferCmd.AddPrometheusPortFlag() msgTransferCmd.AddTransferProgressFlag() if err := msgTransferCmd.Exec(); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go index 77f75cb4e..e0539fa52 100644 --- a/cmd/openim-push/main.go +++ b/cmd/openim-push/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/push" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go index b29efd484..b526c3b86 100644 --- a/cmd/openim-rpc/openim-rpc-auth/main.go +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/auth" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,7 @@ func main() { panic(err.Error()) } if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } + } diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go index f9ac8cd27..bde191c51 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/main.go +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go index 82d71d522..8eeb9c8e1 100644 --- a/cmd/openim-rpc/openim-rpc-friend/main.go +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/friend" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go index 360042f84..a5842ffd1 100644 --- a/cmd/openim-rpc/openim-rpc-group/main.go +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/group" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go index bed57f522..b3895a502 100644 --- a/cmd/openim-rpc/openim-rpc-msg/main.go +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/msg" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go index 4868ce149..8f390bb6a 100644 --- a/cmd/openim-rpc/openim-rpc-third/main.go +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/third" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go index a77a2f768..6994ea2b1 100644 --- a/cmd/openim-rpc/openim-rpc-user/main.go +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -15,12 +15,10 @@ package main import ( - "fmt" - "os" - "github.com/openimsdk/open-im-server/v3/internal/rpc/user" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { @@ -31,7 +29,6 @@ func main() { panic(err.Error()) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v\n\n", err) - os.Exit(-1) + util.ExitWithError(err) } } diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index aeba0a24a..321407f7e 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -18,9 +18,6 @@ import ( "fmt" "time" - "github.com/OpenIMSDK/tools/utils" - "golang.org/x/sync/errgroup" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) @@ -46,20 +43,12 @@ func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error { } hubServer := NewServer(rpcPort, prometheusPort, longServer) - - wg := errgroup.Group{} - wg.Go(func() error { + netDone := make(chan error) + go func() { err = hubServer.Start() if err != nil { - return utils.Wrap1(err) + netDone <- err } - return err - }) - - wg.Go(func() error { - return hubServer.LongConnServer.Run() - }) - - err = wg.Wait() - return err + }() + return hubServer.LongConnServer.Run(netDone) } diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index 01d92b92a..c16da7c64 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -20,12 +20,9 @@ import ( "errors" "fmt" "net/http" - "os" - "os/signal" "strconv" "sync" "sync/atomic" - "syscall" "time" "github.com/OpenIMSDK/tools/apiresp" @@ -49,7 +46,7 @@ import ( ) type LongConnServer interface { - Run() error + Run(done chan error) error wsHandler(w http.ResponseWriter, r *http.Request) GetUserAllCons(userID string) ([]*Client, bool) GetUserPlatformCons(userID string, platform int) ([]*Client, bool, bool) @@ -169,23 +166,20 @@ func NewWsServer(opts ...Option) (*WsServer, error) { }, nil } -func (ws *WsServer) Run() error { +func (ws *WsServer) Run(done chan error) error { var ( - client *Client - wg errgroup.Group - - sigs = make(chan os.Signal, 1) - done = make(chan struct{}, 1) + client *Client + netErr error + shutdownDone = make(chan struct{}, 1) ) server := http.Server{Addr: ":" + utils.IntToString(ws.port), Handler: nil} - wg.Go(func() error { + go func() { for { select { - case <-done: - return nil - + case <-shutdownDone: + return case client = <-ws.registerChan: ws.registerClient(client) case client = <-ws.unregisterChan: @@ -194,33 +188,32 @@ func (ws *WsServer) Run() error { ws.multiTerminalLoginChecker(onlineInfo.clientOK, onlineInfo.oldClients, onlineInfo.newClient) } } - }) - - wg.Go(func() error { - http.HandleFunc("/", ws.wsHandler) - return server.ListenAndServe() - }) - - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - <-sigs - + }() + netDone := make(chan struct{}, 1) go func() { - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - // graceful exit operation for server - _ = server.Shutdown(ctx) - _ = wg.Wait() - close(done) + http.HandleFunc("/", ws.wsHandler) + err := server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + netErr = errs.Wrap(err, "ws start err", server.Addr) + close(netDone) + } }() - + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + var err error select { - case <-done: - return nil - - case <-time.After(15 * time.Second): - return utils.Wrap1(errors.New("timeout exit")) + case err = <-done: + sErr := server.Shutdown(ctx) + if sErr != nil { + return errs.Wrap(sErr, "shutdown err") + } + close(shutdownDone) + if err != nil { + return err + } + case <-netDone: } + return netErr } diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 65518c324..062017f44 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,23 +18,11 @@ import ( "context" "errors" "fmt" - "net/http" - "os" - "os/signal" - "sync" - "syscall" - "time" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mw" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/OpenIMSDK/tools/mw" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -42,12 +30,22 @@ import ( kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "net/http" + "os" + "os/signal" + "syscall" ) type MsgTransfer struct { historyCH *OnlineHistoryRedisConsumerHandler // 这个消费者聚合消息, 订阅的topic:ws2ms_chat, 修改通知发往msg_to_modify topic, 消息存入redis后Incr Redis, 再发消息到ms2pschat topic推送, 发消息到msg_to_mongo topic持久化 historyMongoCH *OnlineHistoryMongoConsumerHandler // mongoDB批量插入, 成功后删除redis中消息,以及处理删除通知消息删除的 订阅的topic: msg_to_mongo - // modifyCH *ModifyMsgConsumerHandler // 负责消费修改消息通知的consumer, 订阅的topic: msg_to_modify + ctx context.Context + cancel context.CancelFunc } func StartTransfer(prometheusPort int) error { @@ -65,10 +63,6 @@ func StartTransfer(prometheusPort int) error { return err } client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) - /* - client, err := openkeeper.NewClient(config.Config.Zookeeper.ZkAddr, config.Config.Zookeeper.Schema, - openkeeper.WithFreq(time.Hour), openkeeper.WithRoundRobin(), openkeeper.WithUserNameAndPassword(config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password), openkeeper.WithTimeout(10), openkeeper.WithLogger(log.NewZkLogger()))*/ if err != nil { return err } @@ -109,27 +103,22 @@ func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcCli } func (m *MsgTransfer) Start(prometheusPort int) error { - ctx := context.Background() fmt.Println("start msg transfer", "prometheusPort:", prometheusPort) if prometheusPort <= 0 { return errs.Wrap(errors.New("prometheusPort not correct")) } + m.ctx, m.cancel = context.WithCancel(context.Background()) - var wg sync.WaitGroup + var ( + netDone = make(chan struct{}, 1) + netErr error + ) - wg.Add(1) - go func() { - defer wg.Done() - - m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyCH) - }() - - wg.Add(1) - go func() { - defer wg.Done() - - m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(ctx, m.historyMongoCH) - }() + onError := func(ctx context.Context, err error, errInfo string) { + log.ZWarn(ctx, errInfo, err) + } + go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH, onError) + go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH, onError) if config.Config.Prometheus.Enable { go func() { @@ -141,30 +130,28 @@ func (m *MsgTransfer) Start(prometheusPort int) error { http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg})) err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil) if err != nil && err != http.ErrServerClosed { - panic(err) + netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", prometheusPort)) + netDone <- struct{}{} } }() } sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - <-sigs - - // graceful close kafka client. - go m.historyCH.historyConsumerGroup.Close() - go m.historyMongoCH.historyConsumerGroup.Close() - - done := make(chan struct{}, 1) - go func() { - wg.Wait() - close(done) - }() - + signal.Notify(sigs, syscall.SIGTERM) select { - case <-done: - log.ZInfo(context.Background(), "msgtrasfer exit successfully") - case <-time.After(15 * time.Second): - log.ZError(context.Background(), "msgtransfer force to exit, timeout 15s", nil) + case <-sigs: + util.SIGUSR1Exit() + // graceful close kafka client. + m.cancel() + m.historyCH.historyConsumerGroup.Close() + m.historyMongoCH.historyConsumerGroup.Close() + + case <-netDone: + m.cancel() + m.historyCH.historyConsumerGroup.Close() + m.historyMongoCH.historyConsumerGroup.Close() + close(netDone) + return netErr } return nil diff --git a/internal/push/callback.go b/internal/push/callback.go index 99a58fb07..a572fa572 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -130,7 +130,7 @@ func callbackBeforeSuperGroupOnlinePush( if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, req, resp, config.Config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil { return err } - return nil + if len(resp.UserIDs) != 0 { *pushToUserIDs = resp.UserIDs } diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index ceab86165..80478de99 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -14,7 +14,10 @@ package push -import "context" +import ( + "context" + "github.com/OpenIMSDK/tools/log" +) type Consumer struct { pushCh ConsumerHandler @@ -32,6 +35,9 @@ func NewConsumer(pusher *Pusher) (*Consumer, error) { } func (c *Consumer) Start() { + onError := func(ctx context.Context, err error, errInfo string) { + log.ZWarn(ctx, errInfo, err) + } + go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh, onError) - go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh) } diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index c1226ce6b..caaf95525 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,8 +16,6 @@ package push import ( "context" - "sync" - "github.com/OpenIMSDK/tools/utils" "google.golang.org/grpc" @@ -58,23 +56,18 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e &groupRpcClient, &msgRpcClient, ) - var wg sync.WaitGroup - wg.Add(2) - go func() { - defer wg.Done() - pbpush.RegisterPushMsgServiceServer(server, &pushServer{ - pusher: pusher, - }) - }() + + pbpush.RegisterPushMsgServiceServer(server, &pushServer{ + pusher: pusher, + }) + consumer, err := NewConsumer(pusher) if err != nil { return err } - go func() { - defer wg.Done() - consumer.Start() - }() - wg.Wait() + + consumer.Start() + return nil } diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index 40e1c0a87..decc1aa82 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -64,7 +64,7 @@ func StartTask() error { crontab.Start() sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + signal.Notify(sigs, syscall.SIGTERM) <-sigs // stop crontab, Wait for the running task to exit. diff --git a/pkg/common/ginprometheus/ginprometheus.go b/pkg/common/ginprometheus/ginprometheus.go index 1ee8f8e34..c2e6bdcca 100644 --- a/pkg/common/ginprometheus/ginprometheus.go +++ b/pkg/common/ginprometheus/ginprometheus.go @@ -197,30 +197,32 @@ func (p *Prometheus) SetListenAddressWithRouter(listenAddress string, r *gin.Eng } // SetMetricsPath set metrics paths. -func (p *Prometheus) SetMetricsPath(e *gin.Engine) { +func (p *Prometheus) SetMetricsPath(e *gin.Engine) error { if p.listenAddress != "" { p.router.GET(p.MetricsPath, prometheusHandler()) - p.runServer() + return p.runServer() } else { e.GET(p.MetricsPath, prometheusHandler()) + return nil } } // SetMetricsPathWithAuth set metrics paths with authentication. -func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) { +func (p *Prometheus) SetMetricsPathWithAuth(e *gin.Engine, accounts gin.Accounts) error { if p.listenAddress != "" { p.router.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler()) - p.runServer() + return p.runServer() } else { e.GET(p.MetricsPath, gin.BasicAuth(accounts), prometheusHandler()) + return nil } } -func (p *Prometheus) runServer() { - go p.router.Run(p.listenAddress) +func (p *Prometheus) runServer() error { + return p.router.Run(p.listenAddress) } func (p *Prometheus) getMetrics() []byte { @@ -366,15 +368,15 @@ func (p *Prometheus) registerMetrics(subsystem string) { } // Use adds the middleware to a gin engine. -func (p *Prometheus) Use(e *gin.Engine) { +func (p *Prometheus) Use(e *gin.Engine) error { e.Use(p.HandlerFunc()) - p.SetMetricsPath(e) + return p.SetMetricsPath(e) } // UseWithAuth adds the middleware to a gin engine with BasicAuth. -func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) { +func (p *Prometheus) UseWithAuth(e *gin.Engine, accounts gin.Accounts) error { e.Use(p.HandlerFunc()) - p.SetMetricsPathWithAuth(e, accounts) + return p.SetMetricsPathWithAuth(e, accounts) } // HandlerFunc defines handler function for middleware. diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 5bff50d88..908b8f088 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,19 +17,16 @@ package kafka import ( "context" "errors" - "strings" - + "fmt" "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "strings" ) type MConsumerGroup struct { - ctx context.Context - cancel context.CancelFunc - sarama.ConsumerGroup groupID string topics []string @@ -57,9 +54,7 @@ func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []str return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, config.Config.Kafka.Username, config.Config.Kafka.Password) } - ctx, cancel := context.WithCancel(context.Background()) return &MConsumerGroup{ - ctx, cancel, consumerGroup, groupID, topics, @@ -70,27 +65,21 @@ func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) contex return GetContextWithMQHeader(cMsg.Headers) } -func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) { - log.ZDebug(context.Background(), "register consumer group", "groupID", mc.groupID) +func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler, onError func(context.Context, error, string)) { + log.ZDebug(ctx, "register consumer group", "groupID", mc.groupID) for { - err := mc.ConsumerGroup.Consume(mc.ctx, mc.topics, handler) - if errors.Is(err, sarama.ErrClosedConsumerGroup) { + err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler) + if errors.Is(err, sarama.ErrClosedConsumerGroup) || errors.Is(err, context.Canceled) { return } - if mc.ctx.Err() != nil { - return - } - if err != nil { - log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID) - } - if ctx.Err() != nil { + errInfo := fmt.Sprintf("consume err: %v, topic: %v, groupID: %s", err, strings.Join(mc.topics, ", "), mc.groupID) + onError(ctx, err, errInfo) // 调用回调函数处理错误 return } } } -func (mc *MConsumerGroup) Close() { - mc.cancel() - mc.ConsumerGroup.Close() +func (mc *MConsumerGroup) Close() error { + return mc.ConsumerGroup.Close() } diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index f6cda2ffb..4b032e9d6 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -15,8 +15,11 @@ package startrpc import ( + "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net" "net/http" "os" @@ -26,14 +29,10 @@ import ( "syscall" "time" - "github.com/OpenIMSDK/tools/errs" - - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "golang.org/x/sync/errgroup" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" @@ -56,12 +55,13 @@ func Start( ) error { fmt.Printf("start %s server, port: %d, prometheusPort: %d, OpenIM version: %s\n", rpcRegisterName, rpcPort, prometheusPort, config.Version) + rpcTcpAddr := net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)) listener, err := net.Listen( "tcp", - net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)), + rpcTcpAddr, ) if err != nil { - return errs.Wrap(err, network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)) + return errs.Wrap(err, "rpc start err", rpcTcpAddr) } defer listener.Close() @@ -108,46 +108,64 @@ func Start( return errs.Wrap(err) } - var wg errgroup.Group - - wg.Go(func() error { + var ( + netDone = make(chan struct{}, 2) + netErr error + httpServer *http.Server + ) + go func() { if config.Config.Prometheus.Enable && prometheusPort != 0 { metric.InitializeMetrics(srv) // Create a HTTP server for prometheus. - httpServer := &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)} - if err := httpServer.ListenAndServe(); err != nil { - fmt.Fprintf(os.Stderr, "\n\nexit -1: \n%+v PrometheusPort: %d \n\n", err, prometheusPort) - os.Exit(-1) + httpServer = &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)} + if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + netErr = errs.Wrap(err, "prometheus start err", httpServer.Addr) + netDone <- struct{}{} } } - return nil - }) + }() - wg.Go(func() error { - return errs.Wrap(srv.Serve(listener)) - }) + go func() { + err := srv.Serve(listener) + if err != nil { + netErr = errs.Wrap(err, "rpc start err: ", rpcTcpAddr) + netDone <- struct{}{} + } + }() sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) - <-sigs - - var ( - done = make(chan struct{}, 1) - gerr error - ) + signal.Notify(sigs, syscall.SIGTERM) + select { + case <-sigs: + util.SIGUSR1Exit() + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + if err := gracefulStopWithCtx(ctx, srv.GracefulStop); err != nil { + return err + } + ctx, cancel = context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + err := httpServer.Shutdown(ctx) + if err != nil { + return errs.Wrap(err, "shutdown err") + } + return errors.New("SIGTERM EXIT") + case <-netDone: + close(netDone) + return netErr + } +} +func gracefulStopWithCtx(ctx context.Context, f func()) error { + done := make(chan struct{}, 1) go func() { - once.Do(srv.GracefulStop) - gerr = wg.Wait() + f() close(done) }() - select { + case <-ctx.Done(): + return errs.Wrap(errors.New("timeout, ctx graceful stop")) case <-done: - return gerr - - case <-time.After(15 * time.Second): - return errs.Wrap(errors.New("timeout exit")) + return nil } - } diff --git a/pkg/util/genutil/genutil.go b/pkg/util/genutil/genutil.go index 0948a7c49..f97b803f6 100644 --- a/pkg/util/genutil/genutil.go +++ b/pkg/util/genutil/genutil.go @@ -15,6 +15,7 @@ package genutil import ( + "errors" "fmt" "os" "path/filepath" @@ -39,3 +40,17 @@ func OutDir(path string) (string, error) { outDir += "/" return outDir, nil } + +func ExitWithError(err error) { + if errors.Is(err, errors.New("SIGTERM EXIT")) { + os.Exit(-1) + } + progName := filepath.Base(os.Args[0]) + fmt.Fprintf(os.Stderr, "\n\n%s exit -1: \n%+v\n\n", progName, err) + os.Exit(-1) +} + +func SIGUSR1Exit() { + progName := filepath.Base(os.Args[0]) + fmt.Printf("\n\n%s receive process terminal SIGTERM exit 0\n\n", progName) +} diff --git a/scripts/githooks/pre-commit.sh b/scripts/githooks/pre-commit.sh index cc756c9ad..d8396b560 100644 --- a/scripts/githooks/pre-commit.sh +++ b/scripts/githooks/pre-commit.sh @@ -105,7 +105,7 @@ fi if [[ ! $local_branch =~ $valid_branch_regex ]] then printError "There is something wrong with your branch name. Branch names in this project must adhere to this contract: $valid_branch_regex. -Your commit will be rejected. You should rename your branch to a valid name(feat/name OR bug/name) and try again." +Your commit will be rejected. You should rename your branch to a valid name(feat/name OR fix/name) and try again." printError "For more on this, read on: https://gist.github.com/cubxxw/126b72104ac0b0ca484c9db09c3e5694" exit 1 fi \ No newline at end of file diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh index ab12735c1..95da16c8a 100755 --- a/scripts/install/openim-push.sh +++ b/scripts/install/openim-push.sh @@ -73,7 +73,7 @@ function openim::push::start() { for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" - nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done openim::util::check_process_names ${SERVER_NAME} diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 7acb1fcdd..1bdb7f640 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -486,7 +486,7 @@ openim::util::stop_services_on_ports() { local pid=$(echo $line | awk '{print $2}') # Try to stop the service by killing its process. - if kill -10 $pid; then + if kill -15 $pid; then stopped+=($port) else not_stopped+=($port) @@ -561,7 +561,7 @@ openim::util::stop_services_with_name() { # If there's a Process ID, it means the service with the name is running. if [[ -n $pid ]]; then # Try to stop the service by killing its process. - if kill -10 $pid 2>/dev/null; then + if kill -15 $pid 2>/dev/null; then stopped_this_time=true fi fi From abdb4941c665894d540dc387910e798e96ded47c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 18 Feb 2024 20:19:25 +0800 Subject: [PATCH 32/74] chore(deps): bump the github-actions group with 20 updates (#1907) Bumps the github-actions group with 20 updates: | Package | From | To | | --- | --- | --- | | [actions/checkout](https://github.com/actions/checkout) | `2` | `4` | | [peter-evans/create-or-update-comment](https://github.com/peter-evans/create-or-update-comment) | `3` | `4` | | [actions/github-script](https://github.com/actions/github-script) | `6` | `7` | | [docker/metadata-action](https://github.com/docker/metadata-action) | `5.0.0` | `5.5.1` | | [codecov/codecov-action](https://github.com/codecov/codecov-action) | `3` | `4` | | [github/codeql-action](https://github.com/github/codeql-action) | `2` | `3` | | [ad-m/github-push-action](https://github.com/ad-m/github-push-action) | `0.6.0` | `0.8.0` | | [actions/dependency-review-action](https://github.com/actions/dependency-review-action) | `3` | `4` | | [actions/cache](https://github.com/actions/cache) | `3` | `4` | | [actions/setup-go](https://github.com/actions/setup-go) | `4` | `5` | | [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) | `3.7.0` | `4.0.0` | | [actions/first-interaction](https://github.com/actions/first-interaction) | `1.2.0` | `1.3.0` | | [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) | `1.8.0` | `1.9.3` | | [peter-evans/create-issue-from-file](https://github.com/peter-evans/create-issue-from-file) | `4` | `5` | | [dessant/lock-threads](https://github.com/dessant/lock-threads) | `4` | `5` | | [actions/setup-node](https://github.com/actions/setup-node) | `2` | `4` | | [alex-page/github-project-automation-plus](https://github.com/alex-page/github-project-automation-plus) | `0.8.3` | `0.9.0` | | [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) | `5` | `6` | | [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) | `5` | `6` | | [actions/stale](https://github.com/actions/stale) | `8` | `9` | Updates `actions/checkout` from 2 to 4 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v2...v4) Updates `peter-evans/create-or-update-comment` from 3 to 4 - [Release notes](https://github.com/peter-evans/create-or-update-comment/releases) - [Commits](https://github.com/peter-evans/create-or-update-comment/compare/v3...v4) Updates `actions/github-script` from 6 to 7 - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v6...v7) Updates `docker/metadata-action` from 5.0.0 to 5.5.1 - [Release notes](https://github.com/docker/metadata-action/releases) - [Commits](https://github.com/docker/metadata-action/compare/v5.0.0...v5.5.1) Updates `codecov/codecov-action` from 3 to 4 - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) Updates `github/codeql-action` from 2 to 3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v2...v3) Updates `ad-m/github-push-action` from 0.6.0 to 0.8.0 - [Release notes](https://github.com/ad-m/github-push-action/releases) - [Commits](https://github.com/ad-m/github-push-action/compare/v0.6.0...v0.8.0) Updates `actions/dependency-review-action` from 3 to 4 - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/v3...v4) Updates `actions/cache` from 3 to 4 - [Release notes](https://github.com/actions/cache/releases) - [Commits](https://github.com/actions/cache/compare/v3...v4) Updates `actions/setup-go` from 4 to 5 - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/v4...v5) Updates `golangci/golangci-lint-action` from 3.7.0 to 4.0.0 - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v3.7.0...v4.0.0) Updates `actions/first-interaction` from 1.2.0 to 1.3.0 - [Release notes](https://github.com/actions/first-interaction/releases) - [Commits](https://github.com/actions/first-interaction/compare/v1.2.0...v1.3.0) Updates `lycheeverse/lychee-action` from 1.8.0 to 1.9.3 - [Release notes](https://github.com/lycheeverse/lychee-action/releases) - [Commits](https://github.com/lycheeverse/lychee-action/compare/v1.8.0...v1.9.3) Updates `peter-evans/create-issue-from-file` from 4 to 5 - [Release notes](https://github.com/peter-evans/create-issue-from-file/releases) - [Commits](https://github.com/peter-evans/create-issue-from-file/compare/v4...v5) Updates `dessant/lock-threads` from 4 to 5 - [Release notes](https://github.com/dessant/lock-threads/releases) - [Changelog](https://github.com/dessant/lock-threads/blob/main/CHANGELOG.md) - [Commits](https://github.com/dessant/lock-threads/compare/v4...v5) Updates `actions/setup-node` from 2 to 4 - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v2...v4) Updates `alex-page/github-project-automation-plus` from 0.8.3 to 0.9.0 - [Release notes](https://github.com/alex-page/github-project-automation-plus/releases) - [Commits](https://github.com/alex-page/github-project-automation-plus/compare/v0.8.3...v0.9.0) Updates `peter-evans/create-pull-request` from 5 to 6 - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/v5...v6) Updates `release-drafter/release-drafter` from 5 to 6 - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6) Updates `actions/stale` from 8 to 9 - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v8...v9) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: peter-evans/create-or-update-comment dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/github-script dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: docker/metadata-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: ad-m/github-push-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: actions/dependency-review-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/cache dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-go dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: golangci/golangci-lint-action dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/first-interaction dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: lycheeverse/lychee-action dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: peter-evans/create-issue-from-file dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: dessant/lock-threads dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/setup-node dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: alex-page/github-project-automation-plus dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: peter-evans/create-pull-request dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/auto-gh-pr.yml | 2 +- .github/workflows/auto-invite.yml | 2 +- .github/workflows/bot-auto-cherry-pick.yml | 2 +- .github/workflows/build-docker-image.yml | 6 ++--- .github/workflows/check-coverage.yml | 2 +- .github/workflows/codeql-analysis.yml | 6 ++--- .github/workflows/create-branch-on-tag.yml | 2 +- .github/workflows/depsreview.yaml | 2 +- .github/workflows/docker-buildx.yml | 28 +++++++++++----------- .github/workflows/e2e-test.yml | 4 ++-- .github/workflows/golangci-lint.yml | 4 ++-- .github/workflows/greetings.yml | 2 +- .github/workflows/help-comment-issue.yml | 2 +- .github/workflows/link-pr.yml | 4 ++-- .github/workflows/lock-issue.yml | 2 +- .github/workflows/milestone.yml | 2 +- .github/workflows/opencommit.yml | 4 ++-- .github/workflows/openimci.yml | 6 ++--- .github/workflows/project-progress.yml | 2 +- .github/workflows/pull-request.yml | 4 ++-- .github/workflows/release-drafter.yml | 2 +- .github/workflows/release.yml | 4 ++-- .github/workflows/stale.yml | 2 +- 23 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/auto-gh-pr.yml b/.github/workflows/auto-gh-pr.yml index d58f35510..45454275e 100644 --- a/.github/workflows/auto-gh-pr.yml +++ b/.github/workflows/auto-gh-pr.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Sync Issue to PR if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main' diff --git a/.github/workflows/auto-invite.yml b/.github/workflows/auto-invite.yml index 0276a33a4..350de30ab 100644 --- a/.github/workflows/auto-invite.yml +++ b/.github/workflows/auto-invite.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Invite user to join our group - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: token: ${{ secrets.BOT_GITHUB_TOKEN }} issue-number: ${{ github.event.issue.number }} diff --git a/.github/workflows/bot-auto-cherry-pick.yml b/.github/workflows/bot-auto-cherry-pick.yml index 7c2ad49ca..baafecad7 100644 --- a/.github/workflows/bot-auto-cherry-pick.yml +++ b/.github/workflows/bot-auto-cherry-pick.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Comment cherry-pick command - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: script: | const pr = context.payload.pull_request; diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml index 619744987..1e9711d2c 100644 --- a/.github/workflows/build-docker-image.yml +++ b/.github/workflows/build-docker-image.yml @@ -43,7 +43,7 @@ jobs: # docker.io/openim/openim-server:latest - name: Extract metadata (tags, labels) for Docker id: meta - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: openim/openim-server # generate Docker tags based on the following events/attributes @@ -86,7 +86,7 @@ jobs: # registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server:latest - name: Extract metadata (tags, labels) for Docker id: meta2 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: registry.cn-hangzhou.aliyuncs.com/openimsdk/openim-server # generate Docker tags based on the following events/attributes @@ -130,7 +130,7 @@ jobs: # ghcr.io/openimsdk/openim-server:latest - name: Extract metadata (tags, labels) for Docker id: meta3 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: ghcr.io/openimsdk/openim-server # generate Docker tags based on the following events/attributes diff --git a/.github/workflows/check-coverage.yml b/.github/workflows/check-coverage.yml index 7b3935730..09d43d7cd 100644 --- a/.github/workflows/check-coverage.yml +++ b/.github/workflows/check-coverage.yml @@ -56,4 +56,4 @@ jobs: continue-on-error: true - name: Upload Coverage to Codecov - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index d6ff39371..29f9382cc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -51,7 +51,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 # Override language selection by uncommenting this and choosing your languages with: languages: go @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). # If this step fails, then you should remove it and run the build manually (see below). - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 \ No newline at end of file + uses: github/codeql-action/analyze@v3 \ No newline at end of file diff --git a/.github/workflows/create-branch-on-tag.yml b/.github/workflows/create-branch-on-tag.yml index b6b9eb8e8..fbacd261b 100644 --- a/.github/workflows/create-branch-on-tag.yml +++ b/.github/workflows/create-branch-on-tag.yml @@ -70,7 +70,7 @@ jobs: - name: Push CHANGELOG to Main if: steps.create-and-commit-changelog.outputs.changes == 'true' - uses: ad-m/github-push-action@v0.6.0 + uses: ad-m/github-push-action@v0.8.0 with: github_token: ${{ secrets.BOT_GITHUB_TOKEN }} branch: main diff --git a/.github/workflows/depsreview.yaml b/.github/workflows/depsreview.yaml index c80929c8c..aff7e3d9e 100644 --- a/.github/workflows/depsreview.yaml +++ b/.github/workflows/depsreview.yaml @@ -15,4 +15,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@v4 - name: 'Dependency Review' - uses: actions/dependency-review-action@v3 \ No newline at end of file + uses: actions/dependency-review-action@v4 \ No newline at end of file diff --git a/.github/workflows/docker-buildx.yml b/.github/workflows/docker-buildx.yml index b3b0b5683..7e7b8229c 100644 --- a/.github/workflows/docker-buildx.yml +++ b/.github/workflows/docker-buildx.yml @@ -40,7 +40,7 @@ jobs: install: true - name: Cache Docker layers - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: ${{ runner.os }}-buildx-${{ github.sha }} @@ -100,7 +100,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-api id: meta1 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-api @@ -131,7 +131,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-cmdutils id: meta2 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-cmdutils @@ -162,7 +162,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-crontask id: meta3 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-crontask @@ -193,7 +193,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-msggateway id: meta4 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-msggateway @@ -224,7 +224,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-msgtransfer id: meta5 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-msgtransfer @@ -255,7 +255,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-push id: meta6 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-push @@ -286,7 +286,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-auth id: meta7 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-auth @@ -317,7 +317,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-conversation id: meta8 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-conversation @@ -348,7 +348,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-friend id: meta9 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-friend @@ -379,7 +379,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-group id: meta10 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-group @@ -410,7 +410,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-msg id: meta11 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-msg @@ -441,7 +441,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-third id: meta12 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-third @@ -472,7 +472,7 @@ jobs: - name: Extract metadata (tags, labels) for Docker openim-rpc-user id: meta13 - uses: docker/metadata-action@v5.0.0 + uses: docker/metadata-action@v5.5.1 with: images: | ghcr.io/openimsdk/openim-rpc-user diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 21bb4986e..6970e7d9c 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -34,7 +34,7 @@ jobs: steps: - name: Set up Go 1.21 - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: 1.21 id: go @@ -60,7 +60,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go ${{ matrix.go_version }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go_version }} id: go diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 250cb96cb..31e491d6b 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,14 +24,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: '1.21' cache: false - name: OpenIM Scripts Verification(make verify) run: sudo make verify - name: golangci-lint - uses: golangci/golangci-lint-action@v3.7.0 + uses: golangci/golangci-lint-action@v4.0.0 with: # Require: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version version: v1.54 diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index a92ed40ad..b1c85ee37 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: actions/first-interaction@v1.2.0 + - uses: actions/first-interaction@v1.3.0 with: repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} pr-message: | diff --git a/.github/workflows/help-comment-issue.yml b/.github/workflows/help-comment-issue.yml index dce858197..c4e72ffc6 100644 --- a/.github/workflows/help-comment-issue.yml +++ b/.github/workflows/help-comment-issue.yml @@ -26,7 +26,7 @@ jobs: issues: write steps: - name: Add comment - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: issue-number: ${{ github.event.issue.number }} token: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/.github/workflows/link-pr.yml b/.github/workflows/link-pr.yml index 768742eee..6c5ff6c5b 100644 --- a/.github/workflows/link-pr.yml +++ b/.github/workflows/link-pr.yml @@ -27,7 +27,7 @@ jobs: - name: Link Checker id: lychee - uses: lycheeverse/lychee-action@v1.8.0 + uses: lycheeverse/lychee-action@v1.9.3 with: # For parameter description, see https://github.com/lycheeverse/lychee#commandline-parameters # Actions Link address -> https://github.com/lycheeverse/lychee-action @@ -45,7 +45,7 @@ jobs: - name: Create Issue From File if: env.lychee_exit_code != 0 - uses: peter-evans/create-issue-from-file@v4 + uses: peter-evans/create-issue-from-file@v5 with: title: Bug reports for links in OpenIM docs content-filepath: ./lychee/out.md diff --git a/.github/workflows/lock-issue.yml b/.github/workflows/lock-issue.yml index 6a9aed9ba..edf280965 100644 --- a/.github/workflows/lock-issue.yml +++ b/.github/workflows/lock-issue.yml @@ -30,7 +30,7 @@ jobs: action: runs-on: ubuntu-latest steps: - - uses: dessant/lock-threads@v4 + - uses: dessant/lock-threads@v5 with: github-token: ${{ secrets.BOT_GITHUB_TOKEN }} issue-inactive-days: '365' diff --git a/.github/workflows/milestone.yml b/.github/workflows/milestone.yml index e0731b25f..ecd64656a 100644 --- a/.github/workflows/milestone.yml +++ b/.github/workflows/milestone.yml @@ -39,7 +39,7 @@ jobs: statuses: none steps: - - uses: actions/github-script@v6 # v6 + - uses: actions/github-script@v7 # v6 with: script: | if (!context.payload.pull_request.merged) { diff --git a/.github/workflows/opencommit.yml b/.github/workflows/opencommit.yml index 629a67aae..d483ef1f6 100644 --- a/.github/workflows/opencommit.yml +++ b/.github/workflows/opencommit.yml @@ -28,10 +28,10 @@ jobs: permissions: write-all steps: - name: Setup Node.js Environment - uses: actions/setup-node@v2 + uses: actions/setup-node@v4 with: node-version: '16' - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: di-sukharev/opencommit@github-action-v1.0.4 diff --git a/.github/workflows/openimci.yml b/.github/workflows/openimci.yml index 2d42e3155..d10033a1c 100644 --- a/.github/workflows/openimci.yml +++ b/.github/workflows/openimci.yml @@ -59,7 +59,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go ${{ matrix.go_version }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go_version }} id: go @@ -191,7 +191,7 @@ jobs: uses: actions/checkout@v4 - name: Set up Go ${{ matrix.go_version }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go_version }} id: go @@ -276,7 +276,7 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go ${{ matrix.go_version }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go_version }} id: go diff --git a/.github/workflows/project-progress.yml b/.github/workflows/project-progress.yml index 0b071b3a8..87a4c1381 100644 --- a/.github/workflows/project-progress.yml +++ b/.github/workflows/project-progress.yml @@ -32,7 +32,7 @@ jobs: move-assigned-card: runs-on: ubuntu-latest steps: - - uses: alex-page/github-project-automation-plus@v0.8.3 + - uses: alex-page/github-project-automation-plus@v0.9.0 with: project: openim-powerful column: In Progress diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6f39c93b2..bc1b094bf 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -33,7 +33,7 @@ jobs: - uses: actions/setup-node@v4 - name: Setup Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 - name: Run go modules tidy run: | sudo apt-get install jq @@ -87,7 +87,7 @@ jobs: continue-on-error: true - name: Create Pull Request - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.BOT_GITHUB_TOKEN }} commit-message: "cicd: bump League Patch" diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 0eed80911..251f55876 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -46,7 +46,7 @@ jobs: # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV # Drafts your next Release notes as Pull Requests are merged into "master" - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # with: # config-name: my-config.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2147c865..9950bdabb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,7 +33,7 @@ jobs: with: fetch-depth: 0 - run: git fetch --force --tags - - uses: actions/setup-go@v4 + - uses: actions/setup-go@v5 with: go-version: stable # More assembly might be required: Docker logins, GPG, etc. It all depends @@ -71,7 +71,7 @@ jobs: version: 3.x repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: docker/setup-qemu-action@326560df218a7ea9cf6ab49bbc88b8b306bb437e # v2 - - uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3 + - uses: actions/cache@a2ed59d39b352305bdd2f628719a53b2cc4f9613 # v3 with: path: | ./_output/dist/*.deb diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index ba767f167..da44cb7f3 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -32,7 +32,7 @@ jobs: pull-requests: write steps: - - uses: actions/stale@v8 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.BOT_GITHUB_TOKEN }} days-before-stale: 60 From 60c446b69408a2c96aef9c5b72c376a7b6efb9b9 Mon Sep 17 00:00:00 2001 From: Seal Bell Date: Mon, 19 Feb 2024 09:35:39 +0800 Subject: [PATCH 33/74] docs:add README_hu.md (#1919) --- docs/readme/README_hu.md | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 docs/readme/README_hu.md diff --git a/docs/readme/README_hu.md b/docs/readme/README_hu.md new file mode 100644 index 000000000..57f006692 --- /dev/null +++ b/docs/readme/README_hu.md @@ -0,0 +1,187 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ Az OpenIM-ről + +Az OpenIM egy szolgáltatási platform, amelyet kifejezetten a csevegés, az audio-video hívások, az értesítések és az AI chatbotok alkalmazásokba történő integrálására terveztek. Számos hatékony API-t és Webhookot kínál, lehetővé téve a fejlesztők számára, hogy ezeket az interaktív szolgáltatásokat könnyen beépítsék alkalmazásaikba. Az OpenIM nem egy önálló csevegőalkalmazás, hanem platformként szolgál más alkalmazások támogatására a gazdag kommunikációs funkciók elérésében. A következő diagram az AppServer, az AppClient, az OpenIMServer és az OpenIMSDK közötti interakciót szemlélteti részletesen. + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 Az OpenIMSDK-ról + +Az **OpenIMSDK** egy **OpenIMServer** számára készült azonnali üzenetküldő SDK, amelyet kifejezetten ügyfélalkalmazásokba való beágyazáshoz hoztak létre. Fő jellemzői és moduljai a következők: + ++ 🌟 Főbb jellemzők: + + - 📦 Helyi raktár + - 🔔 Hallgatói visszahívások + - 🛡️ API-csomagolás + - 🌐 Kapcsolatkezelés + ++ 📚 Fő modulok: + + 1. 🚀 Inicializálás és bejelentkezés + 2. 👤 Felhasználókezelés + 3. 👫 Barátkezelés + 4. 🤖 Csoportfunkciók + 5. 💬 Beszélgetéskezelés + +Golang használatával készült, és támogatja a többplatformos telepítést, biztosítva a konzisztens hozzáférési élményt minden platformon. + +👉 **[Fedezze fel a GO SDK-t](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Az OpenIMServerről + ++ **OpenIMServer** a következő jellemzőkkel rendelkezik: + - 🌐 Mikroszolgáltatási architektúra: Támogatja a fürt módot, beleértve az átjárót és több rpc szolgáltatást. + - 🚀 Változatos telepítési módszerek: Támogatja a forráskódon, Kubernetesen vagy Dockeren keresztül történő telepítést. + - Hatalmas felhasználói bázis támogatása: Szuper nagy csoportok több százezer felhasználóval, több tízmillió felhasználóval és több milliárd üzenettel. + +### Továbbfejlesztett üzleti funkcionalitás: + ++ **REST API**: Az OpenIMServer REST API-kat kínál az üzleti rendszerek számára, amelyek célja, hogy a vállalkozásokat több funkcióval ruházza fel, mint például csoportok létrehozása és push üzenetek küldése háttérfelületeken keresztül. ++ **Webhooks**: Az OpenIMServer visszahívási lehetőségeket biztosít több üzleti forma kiterjesztéséhez. A visszahívás azt jelenti, hogy az OpenIMServer kérelmet küld az üzleti szervernek egy bizonyos esemény előtt vagy után, például visszahívásokat üzenet küldése előtt vagy után. + +👉 **[Tudj meg többet](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Általános építészet + +Merüljön el az Open-IM-Server funkcióinak szívében az architektúra diagramunk segítségével. + +![Overall Architecture](../images/architecture-layers.png) + + +## :rocket: Gyors indítás + +Számos platformot támogatunk. Íme a címek a gyors weboldali használathoz: + +👉 **[OpenIM online webdemó](https://web-enterprise.rentsoft.cn/)** + +🤲 A felhasználói élmény megkönnyítése érdekében különféle telepítési megoldásokat kínálunk. Az alábbi listából választhatja ki a telepítési módot: + ++ **[Forráskód-telepítési útmutató](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Docker telepítési útmutató](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Kubernetes telepítési útmutató](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Mac fejlesztői telepítési útmutató](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Az OpenIM fejlesztésének megkezdéséhez + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM Célunk egy felső szintű nyílt forráskódú közösség felépítése. Van egy szabványkészletünk a [Közösségi adattárban](https://github.com/OpenIMSDK/community). + +Ha hozzá szeretne járulni ehhez az Open-IM-Server adattárhoz, kérjük, olvassa el [közreműködői dokumentációnkat](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + +Mielőtt elkezdené, győződjön meg arról, hogy a változtatásokra van-e igény. Erre a legjobb egy [új beszélgetés](https://github.com/openimsdk/open-im-server/discussions/new/choose) VAGY [Slack Communication](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q)létrehozása, vagy ha problémát talál, először [jelentse](https://github.com/openimsdk/open-im-server/issues/new/choose) first. + +- [OpenIM API referencia](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash naplózás](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD műveletek](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [OpenIM Code-egyezmények](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [OpenIM Commit Guidelines](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [OpenIM fejlesztési útmutató](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [OpenIM címtárszerkezet](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [OpenIM környezet beállítása](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [OpenIM hibakód hivatkozás](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git Cherry Pick Guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub munkafolyamat](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [OpenIM Go Code szabványok](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [OpenIM képre vonatkozó irányelvek](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [OpenIM kezdeti konfiguráció](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [OpenIM Docker telepítési útmutató](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [OpenIM OpenIM Linux rendszertelepítés](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux fejlesztési útmutató](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [OpenIM helyi műveletek útmutatója](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM naplózási egyezmények](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [OpenIM offline telepítés](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc Tools](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [OpenIM tesztelési útmutató](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OpenIM Script Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM verzió](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [A háttérrendszer kezelése és a telepítés figyelése](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Mac Developer Deployment Guide for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Közösség + ++ 📚 [OpenIM közösség](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM érdeklődési csoport](https://github.com/Openim-sigs) ++ 🚀 [Csatlakozz a Slack közösségünkhöz](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Csatlakozz a wechathez](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Közösségi Találkozók + +Szeretnénk, ha bárki bekapcsolódna közösségünkbe és hozzájárulna kódunkhoz, ajándékokat és jutalmakat kínálunk, és szeretettel várjuk, hogy csatlakozzon hozzánk minden csütörtök este. + +Konferenciánk az [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯alatt van, akkor kereshet az Open-IM-Server folyamatban a csatlakozáshoz + +A [GitHub-beszélgetések](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting)minden [kéthetente történő megbeszélésről](https://github.com/openimsdk/open-im-server/discussions/categories/meeting) jegyzeteket készítünk. A találkozók történeti feljegyzései, valamint az értekezletek visszajátszásai a [Google Dokumentumok :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing) webhelyen érhetők el. + +## :eyes: Kik használják az OpenIM-et + +Tekintse meg [felhasználói esettanulmányok](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) oldalunkat a projekt felhasználóinak listájáért. Ne habozzon, hagyjon [📝megjegyzést](https://github.com/openimsdk/open-im-server/issues/379), és ossza meg használati esetét. + +## :page_facing_up: Engedély + +Az OpenIM licence az Apache 2.0 licence alá tartozik. A teljes licencszövegért lásd: [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE). + +Az ebben az [OpenIM](https://github.com/openimsdk/open-im-server) tárolóban az [assets/logo](./assets/logo) és [assets/logo-gif](assets/logo-gif) könyvtárak alatt megjelenő OpenIM logót, beleértve annak változatait és animált változatait, szerzői jogi törvények védik. + +## 🔮 Köszönjük közreműködőinknek! + + + + From 7ebd88e345eba35d1b849e1ddb2c5a0c5ff474ef Mon Sep 17 00:00:00 2001 From: Seal Bell Date: Mon, 19 Feb 2024 09:35:43 +0800 Subject: [PATCH 34/74] docs:add README_fa.md (#1920) --- docs/readme/README_fa.md | 187 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 docs/readme/README_fa.md diff --git a/docs/readme/README_fa.md b/docs/readme/README_fa.md new file mode 100644 index 000000000..49f05cd4c --- /dev/null +++ b/docs/readme/README_fa.md @@ -0,0 +1,187 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## درباره OpenIM Ⓜ️ + +OpenIM یک پلتفرم خدماتی است که به طور خاص برای ادغام چت، تماس های صوتی و تصویری، اعلان ها و چت ربات های هوش مصنوعی در برنامه ها طراحی شده است. این مجموعه ای از API ها و Webhook های قدرتمند را ارائه می دهد که به توسعه دهندگان این امکان را می دهد تا به راحتی این ویژگی های تعاملی را در برنامه های خود بگنجانند. OpenIM یک برنامه چت مستقل نیست، بلکه به عنوان یک پلتفرم برای پشتیبانی از برنامه های کاربردی دیگر در دستیابی به قابلیت های ارتباطی غنی عمل می کند. نمودار زیر تعامل بین AppServer، AppClient، OpenIMServer و OpenIMSDK را برای توضیح جزئیات نشان می دهد. + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 درباره OpenIMSDK + +**OpenIMSDK** یک IM SDK است که برای **OpenIMServer** طراحی شده است که به طور خاص برای جاسازی در برنامه های مشتری ایجاد شده است. ویژگی ها و ماژول های اصلی آن به شرح زیر است: + ++ 🌟 ویژگی های اصلی: + + - 📦 ذخیره سازی محلی + - 🔔 پاسخ تماس شنونده + - 🛡️ بسته بندی API + - 🌐 مدیریت اتصال + ++ 📚 ماژول های اصلی: + + 1. 🚀 مقداردهی اولیه و ورود + 2. 👤 مدیریت کاربر + 3. 👫 مدیریت دوست + 4. 🤖 توابع گروه + 5. 💬 مدیریت مکالمه + +این برنامه با استفاده از Golang ساخته شده است و از استقرار چند پلت فرم پشتیبانی می کند و تجربه دسترسی ثابت را در تمام پلتفرم ها تضمین می کند. + +👉 **[کاوش GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 درباره OpenIMServer + ++ **OpenIMServer** دارای ویژگی های زیر است: + - 🌐 معماری Microservice: از حالت کلاستر، از جمله یک دروازه و چندین سرویس rpc پشتیبانی می کند. + - 🚀 روش‌های استقرار متنوع: از استقرار از طریق کد منبع، Kubernetes یا Docker پشتیبانی می‌کند. + - پشتیبانی از پایگاه عظیم کاربران: گروه های فوق العاده بزرگ با صدها هزار کاربر، ده ها میلیون کاربر و میلیاردها پیام. + +### عملکردهای تجاری پیشرفته: + ++ **REST API**: OpenIMServer APIهای REST را برای سیستم‌های تجاری ارائه می‌کند، با هدف توانمندسازی کسب‌وکارها با قابلیت‌های بیشتر، مانند ایجاد گروه‌ها و ارسال پیام‌های فشار از طریق رابط‌های باطنی. ++ **Webhooks**: OpenIMServer قابلیت های پاسخ به تماس را برای گسترش بیشتر فرم های تجاری ارائه می دهد. پاسخ به تماس به این معنی است که OpenIMServer درخواستی را قبل یا بعد از یک رویداد خاص به سرور تجاری ارسال می کند، مانند تماس های قبل یا بعد از ارسال یک پیام. + +👉 **[بیشتر بدانید](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: معماری کلی + +با نمودار معماری ما به قلب عملکرد Open-IM-Server بپردازید. + +![Overall Architecture](../images/architecture-layers.png) + + +## :rocket: شروع سریع + +ما از بسیاری از پلتفرم ها پشتیبانی می کنیم. در اینجا آدرس هایی برای تجربه سریع در سمت وب آمده است: + +👉 **[نسخه نمایشی وب آنلاین OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 برای تسهیل تجربه کاربر، ما راه حل های مختلف استقرار را ارائه می دهیم. می توانید روش استقرار خود را از لیست زیر انتخاب کنید: + ++ **[راهنمای استقرار کد منبع](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[راهنمای استقرار داکر](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[راهنمای استقرار Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[راهنمای استقرار توسعه دهنده مک](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: برای شروع توسعه OpenIM + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM هدف ما ایجاد یک جامعه منبع باز سطح بالا است. ما مجموعه ای از استانداردها را در [مخزن انجمن](https://github.com/OpenIMSDK/community) داریم.. + +اگر می‌خواهید در این مخزن Open-IM-Server مشارکت کنید، لطفاً [مستندات مشارکت‌کننده](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md) ما را بخوانید. + +قبل از شروع، لطفاً مطمئن شوید که تغییرات شما مورد تقاضا هستند. بهترین کار برای آن این است که یک [بحث جدید](https://github.com/openimsdk/open-im-server/discussions/new/choose) یا [ارتباط اسلک](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ایجاد کنید، یا اگر مشکلی پیدا کردید، ابتدا [آن را گزارش کنید](https://github.com/openimsdk/open-im-server/issues/new/choose). + +- [مرجع OpenIM API](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash Logging](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD Actions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [کنوانسیون کد OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [دستورالعمل های تعهد OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [راهنمای توسعه OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [ساختار دایرکتوری OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [تنظیم محیط OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [مرجع کد خطا OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [راهنمای انتخاب گیلاس OpenIM Git](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub Workflow](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [استانداردهای کد OpenIM Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [دستورالعمل های تصویر OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [پیکربندی اولیه OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [راهنمای نصب OpenIM Docker](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [نصب سیستم OpenIM Linux OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [راهنمای توسعه OpenIM Linux](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [راهنمای اقدامات محلی OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM Logging Conventions](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [استقرار آفلاین OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc Tools](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [راهنمای تست OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile Utilities](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [ابزارهای OpenIM Script](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [نسخه OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [مدیریت استقرار باطن و نظارت](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [راهنمای استقرار توسعه دهنده مک برای OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: انجمن + ++ 📚 [انجمن OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [گروه علاقه OpenIM](https://github.com/Openim-sigs) ++ 🚀 [به انجمن Slack ما بپیوندید](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [به وی چت ما بپیوندید](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: جلسات جامعه + +ما می‌خواهیم هر کسی در انجمن ما مشارکت کند و در کد مشارکت کند، ما هدایا و جوایزی ارائه می‌کنیم، و از شما استقبال می‌کنیم که هر پنجشنبه شب به ما بپیوندید. + +کنفرانس ما در [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯 است، سپس می توانید خط لوله Open-IM-Server را برای پیوستن جستجو کنید. + +ما از هر [جلسه دو هفته‌ای](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) در [بحث‌های GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting) یادداشت‌برداری می‌کنیم، یادداشت‌های جلسه تاریخی ما، و همچنین بازپخش جلسات در [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing) موجود است. + +## :eyes: چه کسانی از OpenIM استفاده می کنند + +صفحه [مطالعات موردی کاربر](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) ما را برای لیستی از کاربران پروژه بررسی کنید. از گذاشتن [نظر📝](https://github.com/openimsdk/open-im-server/issues/379) و به اشتراک گذاری مورد استفاده خود دریغ نکنید. + +## :page_facing_up: مجوز + +OpenIM تحت مجوز Apache 2.0 مجوز دارد. برای متن کامل مجوز به [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) مراجعه کنید. + +نشان‌واره OpenIM، شامل انواع و نسخه‌های متحرک آن، که در این مخزن [OpenIM](https://github.com/openimsdk/open-im-server) تحت فهرست‌های [assets/logo](./assets/logo) و [assets/logo-gif](assets/logo-gif) نمایش داده می‌شود، توسط قوانین حق چاپ محافظت می‌شود. + +## 🔮 با تشکر از همکاران ما! + + + + From 35ad4a9510e6f4d8140569286b89097c6eb833d0 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:19:32 +0800 Subject: [PATCH 35/74] cicd: bump League Patch (#1921) --- cmd/openim-api/main.go | 3 ++- internal/msgtransfer/init.go | 22 +++++++++++++--------- internal/push/consumer_init.go | 1 + internal/push/push_rpc_server.go | 1 + internal/rpc/conversation/conversaion.go | 1 + pkg/authverify/token.go | 3 ++- pkg/common/kafka/consumer_group.go | 4 +++- pkg/common/startrpc/start.go | 11 +++++++---- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index c8746bc20..7a7e06293 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -17,7 +17,6 @@ package main import ( "context" "fmt" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net" "net/http" _ "net/http/pprof" @@ -27,6 +26,8 @@ import ( "syscall" "time" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 062017f44..16d8613db 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,11 +18,24 @@ import ( "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "net/http" + "os" + "os/signal" + "syscall" + "github.com/OpenIMSDK/tools/mw" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -30,15 +43,6 @@ import ( kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "net/http" - "os" - "os/signal" - "syscall" ) type MsgTransfer struct { diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index 80478de99..572afe0eb 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -16,6 +16,7 @@ package push import ( "context" + "github.com/OpenIMSDK/tools/log" ) diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index caaf95525..f558aeec3 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,6 +16,7 @@ package push import ( "context" + "github.com/OpenIMSDK/tools/utils" "google.golang.org/grpc" diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 903ecbb18..8558a23ea 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -50,6 +50,7 @@ type conversationServer struct { conversationDatabase controller.ConversationDatabase conversationNotificationSender *notification.ConversationNotificationSender } + func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( ctx context.Context, req *pbconversation.GetConversationNotReceiveMessageUserIDsReq, diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index b951bf219..97bb03391 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -48,7 +48,8 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { } func IsAppManagerUid(ctx context.Context) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) + return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || + utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) } func CheckAdmin(ctx context.Context) error { diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 908b8f088..5245c6a6f 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -18,12 +18,14 @@ import ( "context" "errors" "fmt" + "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "strings" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type MConsumerGroup struct { diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 4b032e9d6..c5105ec51 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -18,8 +18,6 @@ import ( "context" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net" "net/http" "os" @@ -29,11 +27,16 @@ import ( "syscall" "time" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/OpenIMSDK/tools/errs" + + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" From 026f5255c99336da6f1b8329e9f6627bb30cacaa Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:59:32 +0800 Subject: [PATCH 36/74] docs: README add community (#1922) Signed-off-by: wxuanF <2569456943@qq.com> --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index a91024e49..2614a1c75 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,8 @@ Before you start, please make sure your changes are in demand. The best for that + 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) + 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) + :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) ++ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) ++ 💬 [Follow our Twitter account](https://twitter.com/openimsdk) ## :calendar: Community Meetings From 35154321402621c2ff52a844b69ee871f0661a0b Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:27:00 +0800 Subject: [PATCH 37/74] docs: add README_da.md (#1927) Signed-off-by: wxuanF <2569456943@qq.com> --- docs/readme/README_da.md | 192 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 docs/readme/README_da.md diff --git a/docs/readme/README_da.md b/docs/readme/README_da.md new file mode 100644 index 000000000..1b776ddb8 --- /dev/null +++ b/docs/readme/README_da.md @@ -0,0 +1,192 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ + +## :busts_in_silhouette: Fællesskab + ++ 📚 [OpenIM-fællesskab](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM-interessegruppe](https://github.com/Openim-sigs) ++ 🚀 [Deltag i vores Slack-fællesskab](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Deltag i vores WeChat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) ++ 👫 [Deltag i vores Reddit](https://www.reddit.com/r/OpenIMessaging) ++ 💬 [Følg vores Twitter-konto](https://twitter.com/openimsdk) + + +## Ⓜ️ Om OpenIM + +OpenIM er en serviceplatform designet specifikt til integration af chat, lyd-videoopkald, notifikationer og AI-chatbots i applikationer. Den tilbyder en række kraftfulde API'er og Webhooks, som gør det let for udviklere at integrere disse interaktive funktioner i deres applikationer. OpenIM er ikke en selvstændig chatapplikation, men fungerer snarere som en platform, der understøtter andre applikationer i at opnå omfattende kommunikationsfunktionaliteter. Følgende diagram illustrerer interaktionen mellem AppServer, AppClient, OpenIMServer og OpenIMSDK for at forklare detaljeret. + + + +![App-OpenIM Relationship](../images/oepnim-design.png) + +## 🚀 Om OpenIMSDK + + **OpenIMSDK** er en IM SDK designet til **OpenIMServer**, skabt specifikt til indlejring i klientapplikationer. Dens vigtigste funktioner og moduler er som følger: + ++ 🌟 Hovedfunktioner: + + - 📦 Lokal lagring + - 🔔 Lytter-callbacks + - 🛡️ API-indkapsling + - 🌐 Forbindelsesstyring + + ## 📚 Hovedmoduler: + + 1. 🚀 Initialisering og login + 2. 👤 Brugerstyring + 3. 👫 Venstyring + 4. 🤖 Gruppefunktioner + 5. 💬 Håndtering af samtaler + +Det er bygget ved hjælp af Golang og understøtter tværplatformsudrulning, hvilket sikrer en konsekvent adgangsoplevelse på tværs af alle platforme. + +👉 **[Udforsk GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Om OpenIMServer + ++ **OpenIMServer** har følgende karakteristika: + - 🌐 Mikroservicarkitektur: Understøtter klyngetilstand, inklusive en gateway og flere rpc-tjenester. + - 🚀 Forskellige udrulningsmetoder: Understøtter udrulning via kildekode, Kubernetes eller Docker. + - Støtte til massiv brugerbase: Super store grupper med hundredtusinder af brugere, titusinder af brugere og milliarder af beskeder. + +### Forbedret forretningsfunktionalitet: + ++ **REST API**:OpenIMServer tilbyder REST API'er til forretningssystemer, med det formål at give virksomheder flere funktioner, såsom at oprette grupper og sende push-beskeder gennem backend-grænseflader. ++ **Webhooks**:OpenIMServer giver mulighed for callback-funktionalitet for at udvide flere forretningsformer. Et callback betyder, at OpenIMServer sender en anmodning til forretningsserveren før eller efter en bestemt begivenhed, som callbacks før eller efter at have sendt en besked. + +👉 **[Lær mere](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Samlet Arkitektur + +Dyk ned i hjertet af Open-IM-Servers funktionalitet med vores arkitekturdiagram. + +![Overall Architecture](../images/architecture-layers.png) + +## :rocket: Hurtig start + +Vi understøtter mange platforme. Her er adresserne for hurtig oplevelse på websiden: + +👉 **[OpenIM online demo](https://www.openim.io/zh/commercial)** + +🤲 For at lette brugeroplevelsen tilbyder vi forskellige udrulningsløsninger. Du kan vælge din udrulningsmetode fra listen nedenfor: + ++ **[Vejledning til udrulning af kildekode](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Vejledning til Docker-udrulning](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Vejledning til Kubernetes-udrulning](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Vejledning til Mac-udviklerudrulning](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: For at starte udviklingen af OpenIM + +[![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM Vores mål er at bygge et topniveau åben kildekode-fællesskab. Vi har et sæt standarder i [Community-repositoriet](https://github.com/OpenIMSDK/community). + +Hvis du gerne vil bidrage til dette Open-IM-Server-repositorium, bedes du læse vores [dokumentation for bidragydere](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + +Før du starter, skal du sikre dig, at dine ændringer er efterspurgte. Det bedste for det er at oprette en [ny diskussion](https://github.com/openimsdk/open-im-server/discussions/new/choose) ELLER [Slack-kommunikation](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), eller hvis du finder et problem, [rapportere det](https://github.com/openimsdk/open-im-server/issues/new/choose) først. + +- [OpenIM API-referencer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [OpenIM Bash-logging](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [OpenIM CI/CD-handlinger](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [OpenIM kodekonventioner](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [OpenIM commit-retningslinjer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [OpenIM udviklingsguide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [OpenIM mappestruktur](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [OpenIM miljøopsætning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [OpenIM fejlkode-reference](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [OpenIM Git-arbejdsgang](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [OpenIM Git Cherry Pick-guide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [OpenIM GitHub-arbejdsgang](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [OpenIM Go kode-standarder](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [OpenIM billedretningslinjer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [OpenIM initialkonfiguration](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [OpenIM Docker installationsguide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [OpenIM OpenIM Linux-systeminstallation](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [OpenIM Linux-udviklingsguide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [OpenIM lokale handlingsguide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [OpenIM logningskonventioner](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [OpenIM offline-udrulning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [OpenIM Protoc-værktøjer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [OpenIM testguide](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [OpenIM Utility Go](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [OpenIM Makefile-værktøjer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [OpenIM skriptværktøjer](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [OpenIM versionsstyring](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Administrer backend og overvåg udrulning](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Mac-udviklerudrulningsguide for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :calendar: Fællesskabsmøder + +Vi ønsker, at alle involverer sig i vores fællesskab og bidrager med kode, vi tilbyder gaver og belønninger, og vi byder dig velkommen til at deltage hver torsdag aften. + +Vores konference er på [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, derefter kan du søge Open-IM-Server pipeline for at deltage. + +Vi tager [notater](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) af hvert fjortendages møde i [GitHub-diskussioner](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Vores historiske mødenotater samt genudsendelser af møderne er tilgængelige på [Google Docs](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing) 📑. + +## :eyes: Hvem Bruger OpenIM + +Tjek vores side med [brugercasestudier](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) for en liste over projektbrugerne. Tøv ikke med at efterlade en 📝[kommentar](https://github.com/openimsdk/open-im-server/issues/379) og dele dit brugstilfælde. + +## :page_facing_up: Licens + +OpenIM er licenseret under Apache 2.0-licensen. Se [LICENSE](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) for den fulde licens tekst. + +OpenIM-logoet, inklusive dets variationer og animerede versioner, vist i dette repositorium [OpenIM](https://github.com/openimsdk/open-im-server) under mapperne [assets/logo](../../assets/logo) og [assets/logo-gif](../../assets/logo-gif), er beskyttet af ophavsretslove. + +## 🔮 Tak til vores bidragydere! + + + + \ No newline at end of file From fe0116a811690bcfbf65744fe825a9cf39fd7f29 Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Mon, 19 Feb 2024 20:27:45 +0800 Subject: [PATCH 38/74] add Docs/readme vi (#1925) * docs/README_vi Signed-off-by: longyuqing112 <2631918708@qq.com> * docs/README_vi Signed-off-by: longyuqing112 <2631918708@qq.com> --------- Signed-off-by: longyuqing112 <2631918708@qq.com> --- docs/readme/README_vi.md | 188 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 docs/readme/README_vi.md diff --git a/docs/readme/README_vi.md b/docs/readme/README_vi.md new file mode 100644 index 000000000..e500da6d2 --- /dev/null +++ b/docs/readme/README_vi.md @@ -0,0 +1,188 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ Về OpenIM + +OpenIM là một nền tảng dịch vụ được thiết kế đặc biệt cho việc tích hợp chat, cuộc gọi âm thanh-video, thông báo và chatbot AI vào các ứng dụng. Nó cung cấp một loạt các API mạnh mẽ và Webhooks, giúp các nhà phát triển dễ dàng tích hợp các tính năng tương tác này vào ứng dụng của mình. OpenIM không phải là một ứng dụng chat độc lập, mà là một nền tảng hỗ trợ các ứng dụng khác để đạt được các chức năng giao tiếp phong phú. Sơ đồ sau đây minh họa sự tương tác giữa AppServer, AppClient, OpenIMServer và OpenIMSDK để giải thích chi tiết. + +![App-OpenIM Relationship](../../docs/images/oepnim-design.png) + +## 🚀 Về OpenIMSDK + +**OpenIMSDK** là một SDK IM được thiết kế cho **OpenIMServer**, được tạo ra đặc biệt để nhúng vào các ứng dụng khách. Các tính năng chính và các mô-đun của nó như sau: + ++ 🌟 Các Tính Năng Chính: + + - 📦 Lưu trữ cục bộ + - 🔔 Gọi lại sự kiện (Listener callbacks) + - 🛡️ Bọc API + - 🌐 Quản lý kết nối + ++ 📚 Các Mô-đun Chính: + + 1. 🚀 Khởi tạo và Đăng nhập + 2. 👤 Quản lý Người dùng + 3. 👫 Quản lý Bạn bè + 4. 🤖 Chức năng Nhóm + 5. 💬 Xử lý Cuộc trò chuyện + +Nó được xây dựng bằng Golang và hỗ trợ triển khai đa nền tảng, đảm bảo trải nghiệm truy cập nhất quán trên tất cả các nền tảng + +👉 **[Khám phá GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Về OpenIMServer + ++ **OpenIMServer** có những đặc điểm sau: + - 🌐 Kiến trúc vi dịch vụ: Hỗ trợ chế độ cluster, bao gồm một gateway và nhiều dịch vụ rpc. + - 🚀 Phương pháp triển khai đa dạng: Hỗ trợ triển khai qua mã nguồn, Kubernetes hoặc Docker. + - Hỗ trợ cho cơ sở người dùng lớn: Nhóm siêu lớn với hàng trăm nghìn người dùng, hàng chục triệu người dùng và hàng tỷ tin nhắn. + +### Tăng cường Chức năng Kinh doanh: + ++ **REST API**: OpenIMServer cung cấp REST APIs cho các hệ thống kinh doanh, nhằm tăng cường khả năng cho doanh nghiệp với nhiều chức năng hơn, như tạo nhóm và gửi tin nhắn đẩy qua giao diện backend. ++ **Webhooks**: OpenIMServer cung cấp khả năng gọi lại để mở rộng thêm hình thức kinh doanh. Một gọi lại có nghĩa là OpenIMServer gửi một yêu cầu đến máy chủ kinh doanh trước hoặc sau một sự kiện nhất định, giống như gọi lại trước hoặc sau khi gửi một tin nhắn. + +👉 **[Learn more](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Kiến trúc tổng thể + + Làm sâu sắc vào trái tim của chức năng Open-IM-Server với sơ đồ kiến trúc của chúng tôi. + +![Overall Architecture](../../docs/images/architecture-layers.png) + + +## :rocket: Bắt đầu nhanh + +Chúng tôi hỗ trợ nhiều nền tảng. Dưới đây là các địa chỉ để trải nghiệm nhanh trên phía web: + +👉 **[Demo web trực tuyến OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 Để tạo thuận lợi cho trải nghiệm người dùng, chúng tôi cung cấp các giải pháp triển khai đa dạng. Bạn có thể chọn phương thức triển khai từ danh sách dưới đây: + ++ **[Hướng dẫn Triển khai Mã Nguồn](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[Hướng dẫn Triển khai Docker](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Hướng dẫn Triển khai Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Hướng dẫn Triển khai cho Nhà Phát Triển Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Để Bắt Đầu Phát Triển OpenIM + +[![Mở trong Dev Contain](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +Mục tiêu của OpenIM là xây dựng một cộng đồng mã nguồn mở cấp cao. Chúng tôi có một bộ tiêu chuẩn, Trong [kho lưu trữ Cộng đồng](https://github.com/OpenIMSDK/community). + +Nếu bạn muốn đóng góp cho kho lưu trữ Open-IM-Server này, vui lòng đọc [tài liệu hướng dẫn cho người đóng góp](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + + +Trước khi bạn bắt đầu, hãy chắc chắn rằng các thay đổi của bạn được yêu cầu. Cách tốt nhất là tạo một [cuộc thảo luận mới](https://github.com/openimsdk/open-im-server/discussions/new/choose) hoặc [Giao tiếp Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), hoặc nếu bạn tìm thấy một vấn đề, [báo cáo nó ](https://github.com/openimsdk/open-im-server/issues/new/choose) trước. + +- [Tham khảo API OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Nhật ký Bash OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Hành động CI/CD OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Quy ước Mã OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Hướng dẫn Commit OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Hướng dẫn Phát triển OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Cấu trúc Thư mục OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Cài đặt Môi trường OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Tham khảo Mã Lỗi OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Quy trình Git OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [Hướng dẫn Cherry Pick Git OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Quy trình GitHub OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [Tiêu chuẩn Mã Go OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Hướng dẫn Hình ảnh OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Cấu hình Ban đầu OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Hướng dẫn Cài đặt Docker OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [Hướng dẫn Cài đặt Hệ thống Linux OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [Hướng dẫn Phát triển Linux OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Hướng dẫn Hành động Địa phương OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Quy ước Nhật ký OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Triển khai Ngoại tuyến OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Công cụ Protoc OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Hướng dẫn Kiểm thử OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [Utility Go OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [Tiện ích Makefile OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [Tiện ích Kịch bản OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [Quản lý Phiên bản OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Quản lý triển khai và giám sát backend](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Hướng dẫn Triển khai cho Nhà Phát triển Mac OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Cộng đồng + ++ 📚 [Cộng đồng OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [Nhóm Quan tâm OpenIM](https://github.com/Openim-sigs) ++ 🚀 [Tham gia cộng đồng Slack của chúng tôi](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Tham gia nhóm WeChat của chúng tôi (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Cuộc họp Cộng đồng + +Chúng tôi muốn bất kỳ ai cũng có thể tham gia cộng đồng và đóng góp mã nguồn, chúng tôi cung cấp quà tặng và phần thưởng, và chúng tôi chào đón bạn tham gia cùng chúng tôi mỗi tối thứ Năm. + +Hội nghị của chúng tôi được tổ chức trên Slack của [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, sau đó bạn có thể tìm kiếm pipeline Open-IM-Server để tham gia + +Chúng tôi ghi chú mỗi [cuộc họp hai tuần một lần](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) trong [các cuộc thảo luận GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), ghi chú cuộc họp lịch sử của chúng tôi cũng như các bản ghi lại của cuộc họp có sẵn tại [Google Docs :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Ai Đang Sử Dụng OpenIM + +Xem trangr [các nghiên cứu trường hợp người dùng](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) của chúng tôi để biết danh sách các người dùng dự án. Đừng ngần ngại để lại [📝bình luận](https://github.com/openimsdk/open-im-server/issues/379) và chia sẻ trường hợp sử dụng của bạn. + +## :page_facing_up: Giấy phép + +OpenIM được cấp phép theo giấy phép Apache 2.0. Xem [GIẤY PHÉP](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) để biết toàn bộ nội dung giấy phép. + +Logo OpenIM, bao gồm các biến thể và phiên bản hoạt hình, được hiển thị trong kho lưu trữ này [OpenIM](https://github.com/openimsdk/open-im-server) dưới các thư mục [assets/logo](../../assets/logo) và [assets/logo-gif](assets/logo-gif) được bảo vệ bởi luật bản quyền. + +## 🔮 Cảm ơn các đóng góp của bạn! + + + + From 751497fe751797b6b8ec4e36ff1b1df00b331fa4 Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:18:43 +0800 Subject: [PATCH 39/74] Docs/readme modify oeder (#1924) * docs: README add community Signed-off-by: wxuanF <2569456943@qq.com> * docs: README modify order Signed-off-by: wxuanF <2569456943@qq.com> --------- Signed-off-by: wxuanF <2569456943@qq.com> --- README.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 2614a1c75..d45c6b6fe 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,15 @@

+## :busts_in_silhouette: Community + ++ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) ++ 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) ++ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) ++ 💬 [Follow our Twitter account](https://twitter.com/openimsdk) + ## Ⓜ️ About OpenIM OpenIM is a service platform specifically designed for integrating chat, audio-video calls, notifications, and AI chatbots into applications. It provides a range of powerful APIs and Webhooks, enabling developers to easily incorporate these interactive features into their applications. OpenIM is not a standalone chat application, but rather serves as a platform to support other applications in achieving rich communication functionalities. The following diagram illustrates the interaction between AppServer, AppClient, OpenIMServer, and OpenIMSDK to explain in detail. @@ -154,16 +163,6 @@ Before you start, please make sure your changes are in demand. The best for that - [Manage backend and monitor deployment](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) - [Mac Developer Deployment Guide for OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) - -## :busts_in_silhouette: Community - -+ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community) -+ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) -+ 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) -+ :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) -+ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) -+ 💬 [Follow our Twitter account](https://twitter.com/openimsdk) - ## :calendar: Community Meetings We want anyone to get involved in our community and contributing code, we offer gifts and rewards, and we welcome you to join us every Thursday night. From 01886eee06d6702130ff2714f25cf9751451d724 Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:19:15 +0800 Subject: [PATCH 40/74] docs/README_el (#1929) Signed-off-by: longyuqing112 <2631918708@qq.com> --- docs/readme/README_el.md | 186 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) create mode 100644 docs/readme/README_el.md diff --git a/docs/readme/README_el.md b/docs/readme/README_el.md new file mode 100644 index 000000000..252521f35 --- /dev/null +++ b/docs/readme/README_el.md @@ -0,0 +1,186 @@ +

+ + + +

+ +
+ +[![Stars](https://img.shields.io/github/stars/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=ff69b4)](https://github.com/openimsdk/open-im-server/stargazers) +[![Forks](https://img.shields.io/github/forks/openimsdk/open-im-server?style=for-the-badge&logo=github&colorB=blue)](https://github.com/openimsdk/open-im-server/network/members) +[![Codecov](https://img.shields.io/codecov/c/github/openimsdk/open-im-server?style=for-the-badge&logo=codecov&colorB=orange)](https://app.codecov.io/gh/openimsdk/open-im-server) +[![Go Report Card](https://goreportcard.com/badge/github.com/openimsdk/open-im-server?style=for-the-badge)](https://goreportcard.com/report/github.com/openimsdk/open-im-server) +[![Go Reference](https://img.shields.io/badge/Go%20Reference-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://pkg.go.dev/github.com/openimsdk/open-im-server/v3) +[![License](https://img.shields.io/badge/license-Apache--2.0-green?style=for-the-badge)](https://github.com/openimsdk/open-im-server/blob/main/LICENSE) +[![Slack](https://img.shields.io/badge/Slack-500%2B-blueviolet?style=for-the-badge&logo=slack&logoColor=white)](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) +[![Best Practices](https://img.shields.io/badge/Best%20Practices-purple?style=for-the-badge)](https://www.bestpractices.dev/projects/8045) +[![Good First Issues](https://img.shields.io/github/issues/openimsdk/open-im-server/good%20first%20issue?style=for-the-badge&logo=github)](https://github.com/openimsdk/open-im-server/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) +[![Language](https://img.shields.io/badge/Language-Go-blue.svg?style=for-the-badge&logo=go&logoColor=white)](https://golang.org/) + + +

+ Englist · + 中文 · + Українська · + Česky · + Magyar · + Español · + فارسی · + Français · + Deutsch · + Polski · + Indonesian · + Suomi · + മലയാളം · + 日本語 · + Nederlands · + Italiano · + Русский · + Português (Brasil) · + Esperanto · + 한국어 · + العربي · + Tiếng Việt · + Dansk · + Ελληνικά · + Türkçe +

+ + +
+ +

+ +## Ⓜ️ Σχετικά με το OpenIM + +Το OpenIM είναι μια πλατφόρμα υπηρεσιών σχεδιασμένη ειδικά για την ενσωμάτωση συνομιλίας, κλήσεων ήχου-βίντεο, ειδοποιήσεων και chatbots AI σε εφαρμογές. Παρέχει μια σειρά από ισχυρά API και Webhooks, επιτρέποντας στους προγραμματιστές να ενσωματώσουν εύκολα αυτές τις αλληλεπιδραστικές λειτουργίες στις εφαρμογές τους. Το OpenIM δεν είναι μια αυτόνομη εφαρμογή συνομιλίας, αλλά λειτουργεί ως πλατφόρμα υποστήριξης άλλων εφαρμογών για την επίτευξη πλούσιων λειτουργιών επικοινωνίας. Το παρακάτω διάγραμμα απεικονίζει την αλληλεπίδραση μεταξύ AppServer, AppClient, OpenIMServer και OpenIMSDK για να εξηγήσει αναλυτικά. + +![App-OpenIM Relationship](../../docs/images/oepnim-design.png) + +## 🚀 Σχετικά με το OpenIMSDK + +Το **OpenIMSDK** είναι ένα SDK για αμεση ανταλλαγή μηνυμάτων σχεδιασμένο για το **OpenIMServer**, δημιουργήθηκε ειδικά για ενσωμάτωση σε εφαρμογές πελατών. Οι κύριες δυνατότητες και μονάδες του είναι οι εξής: + ++ 🌟 Κύριες Δυνατότητες: + + - 📦 Τοπική αποθήκευση + - 🔔 Callbacks ακροατών + - 🛡️ Περιτύλιγμα API + - 🌐 Διαχείριση σύνδεσης + ++ 📚 Κύριες Μονάδες: + + 1. 🚀 Αρχικοποίηση και Σύνδεση + 2. 👤 Διαχείριση Χρηστών + 3. 👫 Διαχείριση Φίλων + 4. 🤖 Λειτουργίες Ομάδας + 5. 💬 Διαχείριση Συνομιλιών + +Είναι κατασκευασμένο χρησιμοποιώντας Golang και υποστηρίζει διασταυρούμενη πλατφόρμα ανάπτυξης, διασφαλίζοντας μια συνεπή εμπειρία πρόσβασης σε όλες τις πλατφόρμες. + +👉 **[Εξερευνήστε το GO SDK](https://github.com/openimsdk/openim-sdk-core)** + +## 🌐 Σχετικά με το OpenIMServer + ++ Το **OpenIMServer** έχει τις ακόλουθες χαρακτηριστικές: + - 🌐 Αρχιτεκτονική μικροϋπηρεσιών: Υποστηρίζει λειτουργία σε σύμπλεγμα, περιλαμβάνοντας έναν πύλη και πολλαπλές υπηρεσίες rpc. + - 🚀 Διάφοροι τρόποι ανάπτυξης: Υποστηρίζει ανάπτυξη μέσω πηγαίου κώδικα, Kubernetes, ή Docker. + - Υποστήριξη για τεράστια βάση χρηστών: Πολύ μεγάλες ομάδες με εκατοντάδες χιλιάδες χρήστες, δεκάδες εκατομμύρια χρήστες και δισεκατομμύρια μηνύματα. + +### Ενισχυμένη Επιχειρηματική Λειτουργικότητα: + ++ **REST API**: Το OpenIMServer προσφέρει REST APIs για επιχειρηματικά συστήματα, με στόχο την ενδυνάμωση των επιχειρήσεων με περισσότερες λειτουργικότητες, όπως η δημιουργία ομάδων και η αποστολή μηνυμάτων push μέσω backend διεπαφών. ++ **Webhooks**: Το OpenIMServer παρέχει δυνατότητες επανάκλησης για την επέκταση περισσότερων επιχειρηματικών μορφών. Μια επανάκληση σημαίνει ότι το OpenIMServer στέλνει ένα αίτημα στον επιχειρηματικό διακομιστή πριν ή μετά από ένα συγκεκριμένο γεγονός, όπως επανακλήσεις πριν ή μετά την αποστολή ενός μηνύματος. + +👉 **[Μάθετε περισσότερα](https://docs.openim.io/guides/introduction/product)** + +## :building_construction: Συνολική Αρχιτεκτονική + +Εξερευνήστε σε βάθος τη λειτουργικότητα του Open-IM-Server με το διάγραμμα αρχιτεκτονικής μας. + +![Overall Architecture](../../docs/images/architecture-layers.png) + + +## :rocket: Γρήγορη Εκκίνηση + +Υποστηρίζουμε πολλές πλατφόρμες. Εδώ είναι οι διευθύνσεις για γρήγορη εμπειρία στην πλευρά του διαδικτύου: + +👉 **[Διαδικτυακή επίδειξη του OpenIM](https://web-enterprise.rentsoft.cn/)** + +🤲 Για να διευκολύνουμε την εμπειρία του χρήστη, προσφέρουμε διάφορες λύσεις ανάπτυξης. Μπορείτε να επιλέξετε τη μέθοδο ανάπτυξης σας από την παρακάτω λίστα: + ++ **[Οδηγός Ανάπτυξης Κώδικα Πηγής](https://docs.openim.io/guides/gettingStarted/imSourceCodeDeployment)** ++ **[δηγός Ανάπτυξης μέσω Docker](https://docs.openim.io/guides/gettingStarted/dockerCompose)** ++ **[Οδηγός Ανάπτυξης Kubernetes](https://docs.openim.io/guides/gettingStarted/k8s-deployment)** ++ **[Οδηγός Ανάπτυξης για Αναπτυξιακούς στο Mac](https://docs.openim.io/guides/gettingstarted/mac-deployment-guide)** + +## :hammer_and_wrench: Για να Αρχίσετε την Ανάπτυξη του OpenIM + +[![Άνοιγμα σε Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) + +OpenIM Στόχος μας είναι να δημιουργήσουμε μια κορυφαίου επιπέδου ανοιχτή πηγή κοινότητας. Διαθέτουμε ένα σύνολο προτύπων, στο [Αποθετήριο Κοινότητας](https://github.com/OpenIMSDK/community). + +Εάν θέλετε να συνεισφέρετε σε αυτό το αποθετήριο Open-IM-Server, παρακαλούμε διαβάστε την [τεκμηρίωση συνεισφέροντος](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). + +Πριν ξεκινήσετε, παρακαλούμε βεβαιωθείτε ότι οι αλλαγές σας είναι ζητούμενες. Το καλύτερο για αυτό είναι να δημιουργήσετε ένα [νέα συζήτηση](https://github.com/openimsdk/open-im-server/discussions/new/choose) ή [Επικοινωνία Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q), ή αν βρείτε ένα ζήτημα, [αναφέρετέ το](https://github.com/openimsdk/open-im-server/issues/new/choose) πρώτα. + +- [Αναφορά API του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/api.md) +- [Καταγραφή Bash του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/bash-log.md) +- [Ενέργειες CI/CD του OpenIMs](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/cicd-actions.md) +- [Συμβάσεις Κώδικα του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/code-conventions.md) +- [Οδηγίες Commit του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/commit.md) +- [Οδηγός Ανάπτυξης του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/development.md) +- [Δομή Καταλόγου του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/directory.md) +- [Ρύθμιση Περιβάλλοντος του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/environment.md) +- [Αναφορά Κωδικών Σφάλματος του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/error-code.md) +- [Ροή Εργασίας Git του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/git-workflow.md) +- [Οδηγός Cherry Pick του Git του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/gitcherry-pick.md) +- [Ροή Εργασίας GitHub του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/github-workflow.md) +- [Πρότυπα Κώδικα Go του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/go-code.md) +- [Οδηγίες Εικόνας του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/images.md) +- [Αρχική Διαμόρφωση του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/init-config.md) +- [Οδηγός Εγκατάστασης Docker του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-docker.md) +- [Οδηγός Εγκατάστασης Συστήματος Linux του Open](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/install-openim-linux-system.md) +- [Οδηγός Ανάπτυξης Linux του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/linux-development.md) +- [Οδηγός Τοπικών Δράσεων του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/local-actions.md) +- [Συμβάσεις Καταγραφής του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/logging.md) +- [Αποστολή Εκτός Σύνδεσης του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/offline-deployment.md) +- [Εργαλεία Protoc του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/protoc-tools.md) +- [Οδηγός Δοκιμών του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/test.md) +- [Χρησιμότητα Go του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-go.md) +- [Χρησιμότητες Makefile του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-makefile.md) +- [Χρησιμότητες Σεναρίου του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/util-scripts.md) +- [Έκδοση του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/version.md) +- [Διαχείριση backend και παρακολούθηση ανάπτυξης](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/prometheus-grafana.md) +- [Οδηγός Ανάπτυξης για Προγραμματιστές Mac του OpenIM](https://github.com/openimsdk/open-im-server/tree/main/docs/contrib/mac-developer-deployment-guide.md) + + +## :busts_in_silhouette: Κοινότητα + ++ 📚 [Κοινότητα OpenIM](https://github.com/OpenIMSDK/community) ++ 💕 [Ομάδα Ενδιαφέροντος OpenIM](https://github.com/Openim-sigs) ++ 🚀 [Εγγραφείτε στην κοινότητα Slack μας](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) ++ :eyes: [γγραφείτε στην ομάδα μας wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + +## :calendar: Συναντήσεις της κοινότητας + +Θέλουμε οποιονδήποτε να εμπλακεί στην κοινότητά μας και να συνεισφέρει κώδικα. Προσφέρουμε δώρα και ανταμοιβές και σας καλωσορίζουμε να μας ενταχθείτε κάθε Πέμπτη βράδυ. + +Η διάσκεψή μας είναι στο [OpenIM Slack](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) 🎯, στη συνέχεια μπορείτε να αναζητήσετε τη διαδικασία Open-IM-Server για να συμμετάσχετε + +Κάνουμε σημειώσεις για κάθε μια [Σημειώνουμε κάθε διμηνιαία συνάντηση](https://github.com/orgs/OpenIMSDK/discussions/categories/meeting) στις [συζητήσεις του GitHub](https://github.com/openimsdk/open-im-server/discussions/categories/meeting), Οι ιστορικές μας σημειώσεις συναντήσεων, καθώς και οι επαναλήψεις των συναντήσεων είναι διαθέσιμες στο[Έγγραφα της Google :bookmark_tabs:](https://docs.google.com/document/d/1nx8MDpuG74NASx081JcCpxPgDITNTpIIos0DS6Vr9GU/edit?usp=sharing). + +## :eyes: Ποιοί Χρησιμοποιούν το OpenIM + +Ελέγξτε τη σελίδα με τις [μελέτες περίπτωσης χρήσης ](https://github.com/OpenIMSDK/community/blob/main/ADOPTERS.md) μας για μια λίστα των χρηστών του έργου. Μην διστάσετε να αφήσετε ένα[📝σχόλιο](https://github.com/openimsdk/open-im-server/issues/379) και να μοιραστείτε την περίπτωση χρήσης σας. +## :page_facing_up: Άδεια Χρήσης + +Το OpenIM διατίθεται υπό την άδεια Apache 2.0. Δείτε τη [ΑΔΕΙΑ ΧΡΗΣΗΣ](https://github.com/openimsdk/open-im-server/tree/main/LICENSE) για το πλήρες κείμενο της άδειας. + +Το λογότυπο του OpenIM, συμπεριλαμβανομένων των παραλλαγών και των κινούμενων εικόνων, που εμφανίζονται σε αυτό το αποθετήριο[OpenIM](https://github.com/openimsdk/open-im-server) υπό τις διευθύνσεις [assets/logo](../../assets/logo) και [assets/logo-gif](../../assets/logo-gif) προστατεύονται από τους νόμους περί πνευματικής ιδιοκτησίας. + +## 🔮 Ευχαριστούμε τους συνεισφέροντες μας! + + + + From d5d2803e76b44c4316417aad3fb78e99272e4bf5 Mon Sep 17 00:00:00 2001 From: chao <48119764+withchao@users.noreply.github.com> Date: Tue, 20 Feb 2024 20:56:29 +0800 Subject: [PATCH 41/74] feat: optimize server code (#1931) * fix: GroupApplicationAcceptedNotification * fix: GroupApplicationAcceptedNotification * fix: NotificationUserInfoUpdate * cicd: robot automated Change * utils.Wrap -> errs.Wrap * utils.Wrap -> errs.Wrap --------- Co-authored-by: withchao --- go.mod | 2 +- go.sum | 4 +- internal/msggateway/client.go | 15 ++--- internal/msggateway/compressor.go | 21 ++++--- internal/msggateway/encoder.go | 5 +- internal/push/offlinepush/getui/push.go | 4 +- internal/rpc/auth/auth.go | 6 +- internal/rpc/group/group.go | 2 +- pkg/common/db/cache/meta_cache.go | 4 +- pkg/common/db/cache/msg.go | 77 +++++++++++++++---------- pkg/common/db/controller/auth.go | 5 +- pkg/common/db/controller/msg.go | 2 +- pkg/common/db/unrelation/msg.go | 18 +++--- pkg/common/db/unrelation/user.go | 3 +- pkg/common/http/http_client.go | 20 +++---- pkg/common/kafka/producer.go | 11 ++-- 16 files changed, 101 insertions(+), 98 deletions(-) diff --git a/go.mod b/go.mod index be835c0a4..ac02683b4 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.55 - github.com/OpenIMSDK/tools v0.0.35 + github.com/OpenIMSDK/tools v0.0.36 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index c4b661f1c..38596d45e 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/IBM/sarama v1.42.2 h1:VoY4hVIZ+WQJ8G9KNY/SQlWguBQXQ9uvFPOnrcu8hEw= github.com/IBM/sarama v1.42.2/go.mod h1:FLPGUGwYqEs62hq2bVG6Io2+5n+pS6s/WOXVKWSLFtE= github.com/OpenIMSDK/protocol v0.0.55 h1:eBjg8DyuhxGmuCUjpoZjg6MJJJXU/xJ3xJwFhrn34yA= github.com/OpenIMSDK/protocol v0.0.55/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.35 h1:YH8UYoaErXqfNrwpUvQxe8nhL++gFH6qCisQPyzk0w8= -github.com/OpenIMSDK/tools v0.0.35/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= +github.com/OpenIMSDK/tools v0.0.36 h1:BT0q64l4f3QJDW16Rc0uJYt1gQFkiPoUQYQ33vo0EcE= +github.com/OpenIMSDK/tools v0.0.36/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 43047fd73..dfd2b03d3 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "runtime/debug" "sync" "sync/atomic" @@ -173,7 +174,7 @@ func (c *Client) handleMessage(message []byte) error { var err error message, err = c.longConnServer.DecompressWithPool(message) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } } @@ -182,15 +183,15 @@ func (c *Client) handleMessage(message []byte) error { err := c.longConnServer.Decode(message, binaryReq) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } if err := c.longConnServer.Validate(binaryReq); err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } if binaryReq.SendID != c.UserID { - return utils.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String()) + return errs.Wrap(errors.New("exception conn userID not same to req userID"), binaryReq.String()) } ctx := mcontext.WithMustInfoCtx( @@ -313,7 +314,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error { encodedBuf, err := c.longConnServer.Encode(resp) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } c.w.Lock() @@ -323,7 +324,7 @@ func (c *Client) writeBinaryMsg(resp Resp) error { if c.IsCompress { resultBuf, compressErr := c.longConnServer.CompressWithPool(encodedBuf) if compressErr != nil { - return utils.Wrap(compressErr, "") + return errs.Wrap(compressErr) } return c.conn.WriteMessage(MessageBinary, resultBuf) } @@ -341,7 +342,7 @@ func (c *Client) writePongMsg() error { err := c.conn.SetWriteDeadline(writeWait) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } return c.conn.WriteMessage(PongMessage, nil) diff --git a/internal/msggateway/compressor.go b/internal/msggateway/compressor.go index ae5e9cdd0..9bbec1ec9 100644 --- a/internal/msggateway/compressor.go +++ b/internal/msggateway/compressor.go @@ -18,10 +18,9 @@ import ( "bytes" "compress/gzip" "errors" + "github.com/OpenIMSDK/tools/errs" "io" "sync" - - "github.com/OpenIMSDK/tools/utils" ) var ( @@ -47,10 +46,10 @@ func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) { gzipBuffer := bytes.Buffer{} gz := gzip.NewWriter(&gzipBuffer) if _, err := gz.Write(rawData); err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } if err := gz.Close(); err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } return gzipBuffer.Bytes(), nil } @@ -63,10 +62,10 @@ func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) { gz.Reset(&gzipBuffer) if _, err := gz.Write(rawData); err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } if err := gz.Close(); err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } return gzipBuffer.Bytes(), nil } @@ -75,11 +74,11 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) { buff := bytes.NewBuffer(compressedData) reader, err := gzip.NewReader(buff) if err != nil { - return nil, utils.Wrap(err, "NewReader failed") + return nil, errs.Wrap(err, "NewReader failed") } compressedData, err = io.ReadAll(reader) if err != nil { - return nil, utils.Wrap(err, "ReadAll failed") + return nil, errs.Wrap(err, "ReadAll failed") } _ = reader.Close() return compressedData, nil @@ -88,18 +87,18 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) { func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, error) { reader := gzipReaderPool.Get().(*gzip.Reader) if reader == nil { - return nil, errors.New("NewReader failed") + return nil, errs.Wrap(errors.New("NewReader failed")) } defer gzipReaderPool.Put(reader) err := reader.Reset(bytes.NewReader(compressedData)) if err != nil { - return nil, utils.Wrap(err, "NewReader failed") + return nil, errs.Wrap(err, "NewReader failed") } compressedData, err = io.ReadAll(reader) if err != nil { - return nil, utils.Wrap(err, "ReadAll failed") + return nil, errs.Wrap(err, "ReadAll failed") } _ = reader.Close() return compressedData, nil diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go index c5f1d00a8..2c46a774b 100644 --- a/internal/msggateway/encoder.go +++ b/internal/msggateway/encoder.go @@ -17,8 +17,7 @@ package msggateway import ( "bytes" "encoding/gob" - - "github.com/OpenIMSDK/tools/utils" + "github.com/OpenIMSDK/tools/errs" ) type Encoder interface { @@ -47,7 +46,7 @@ func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error { dec := gob.NewDecoder(buff) err := dec.Decode(decodeData) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } return nil } diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index b657c9c23..8115e4efb 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -34,8 +34,6 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" - - "github.com/OpenIMSDK/tools/utils" ) var ( @@ -137,7 +135,7 @@ func (g *Client) GetTaskID(ctx context.Context, token string, pushReq PushReq) ( pushReq.Settings = &Settings{TTL: &ttl} err := g.request(ctx, taskURL, pushReq, token, &respTask) if err != nil { - return "", utils.Wrap(err, "") + return "", errs.Wrap(err) } return respTask.TaskID, nil } diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index eaf63f868..1b10ba06a 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -29,8 +29,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/tokenverify" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -105,7 +103,7 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) { claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret()) if err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } m, err := s.authDatabase.GetTokensWithoutError(ctx, claims.UserID, claims.PlatformID) if err != nil { @@ -121,7 +119,7 @@ func (s *authServer) parseToken(ctx context.Context, tokensString string) (claim case constant.KickedToken: return nil, errs.ErrTokenKicked.Wrap() default: - return nil, utils.Wrap(errs.ErrTokenUnknown, "") + return nil, errs.Wrap(errs.ErrTokenUnknown) } } return nil, errs.ErrTokenNotExist.Wrap() diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 1d068b1b2..95f82266f 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -956,7 +956,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf return nil, err } if group.Status == constant.GroupStatusDismissed { - return nil, utils.Wrap(errs.ErrDismissedAlready, "") + return nil, errs.Wrap(errs.ErrDismissedAlready) } resp := &pbgroup.SetGroupInfoResp{} count, err := s.db.FindGroupMemberNum(ctx, group.GroupID) diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 4bc2a046a..7eb486c9a 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -134,7 +134,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin } bs, err := json.Marshal(t) if err != nil { - return "", utils.Wrap(err, "") + return "", errs.Wrap(err) } write = true @@ -153,7 +153,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin if err != nil { log.ZError(ctx, "cache json.Unmarshal failed", err, "key", key, "value", v, "expire", expire) - return t, utils.Wrap(err, "") + return t, errs.Wrap(err) } return t, nil diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 5cd3cb22c..8a54e1a8b 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -149,11 +149,15 @@ func (c *msgCache) getHasReadSeqKey(conversationID string, userID string) string } func (c *msgCache) setSeq(ctx context.Context, conversationID string, seq int64, getkey func(conversationID string) string) error { - return utils.Wrap1(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err()) + return errs.Wrap(c.rdb.Set(ctx, getkey(conversationID), seq, 0).Err()) } func (c *msgCache) getSeq(ctx context.Context, conversationID string, getkey func(conversationID string) string) (int64, error) { - return utils.Wrap2(c.rdb.Get(ctx, getkey(conversationID)).Int64()) + val, err := c.rdb.Get(ctx, getkey(conversationID)).Int64() + if err != nil { + return 0, errs.Wrap(err) + } + return val, nil } func (c *msgCache) getSeqs(ctx context.Context, items []string, getkey func(s string) string) (m map[string]int64, err error) { @@ -214,7 +218,11 @@ func (c *msgCache) getConversationUserMinSeqKey(conversationID, userID string) s } func (c *msgCache) GetConversationUserMinSeq(ctx context.Context, conversationID string, userID string) (int64, error) { - return utils.Wrap2(c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64()) + val, err := c.rdb.Get(ctx, c.getConversationUserMinSeqKey(conversationID, userID)).Int64() + if err != nil { + return 0, errs.Wrap(err) + } + return val, nil } func (c *msgCache) GetConversationUserMinSeqs(ctx context.Context, conversationID string, userIDs []string) (m map[string]int64, err error) { @@ -224,7 +232,7 @@ func (c *msgCache) GetConversationUserMinSeqs(ctx context.Context, conversationI } func (c *msgCache) SetConversationUserMinSeq(ctx context.Context, conversationID string, userID string, minSeq int64) error { - return utils.Wrap1(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err()) + return errs.Wrap(c.rdb.Set(ctx, c.getConversationUserMinSeqKey(conversationID, userID), minSeq, 0).Err()) } func (c *msgCache) SetConversationUserMinSeqs(ctx context.Context, conversationID string, seqs map[string]int64) (err error) { @@ -240,7 +248,7 @@ func (c *msgCache) SetUserConversationsMinSeqs(ctx context.Context, userID strin } func (c *msgCache) SetHasReadSeq(ctx context.Context, userID string, conversationID string, hasReadSeq int64) error { - return utils.Wrap1(c.rdb.Set(ctx, c.getHasReadSeqKey(conversationID, userID), hasReadSeq, 0).Err()) + return errs.Wrap(c.rdb.Set(ctx, c.getHasReadSeqKey(conversationID, userID), hasReadSeq, 0).Err()) } func (c *msgCache) SetHasReadSeqs(ctx context.Context, conversationID string, hasReadSeqs map[string]int64) error { @@ -262,12 +270,15 @@ func (c *msgCache) GetHasReadSeqs(ctx context.Context, userID string, conversati } func (c *msgCache) GetHasReadSeq(ctx context.Context, userID string, conversationID string) (int64, error) { - return utils.Wrap2(c.rdb.Get(ctx, c.getHasReadSeqKey(conversationID, userID)).Int64()) + val, err := c.rdb.Get(ctx, c.getHasReadSeqKey(conversationID, userID)).Int64() + if err != nil { + return 0, err + } + return val, nil } func (c *msgCache) AddTokenFlag(ctx context.Context, userID string, platformID int, token string, flag int) error { key := uidPidToken + userID + ":" + constant.PlatformIDToName(platformID) - return errs.Wrap(c.rdb.HSet(ctx, key, token, flag).Err()) } @@ -694,7 +705,11 @@ func (c *msgCache) SetGetuiToken(ctx context.Context, token string, expireTime i } func (c *msgCache) GetGetuiToken(ctx context.Context) (string, error) { - return utils.Wrap2(c.rdb.Get(ctx, getuiToken).Result()) + val, err := c.rdb.Get(ctx, getuiToken).Result() + if err != nil { + return "", errs.Wrap(err) + } + return val, nil } func (c *msgCache) SetGetuiTaskID(ctx context.Context, taskID string, expireTime int64) error { @@ -702,7 +717,11 @@ func (c *msgCache) SetGetuiTaskID(ctx context.Context, taskID string, expireTime } func (c *msgCache) GetGetuiTaskID(ctx context.Context) (string, error) { - return utils.Wrap2(c.rdb.Get(ctx, getuiTaskID).Result()) + val, err := c.rdb.Get(ctx, getuiTaskID).Result() + if err != nil { + return "", errs.Wrap(err) + } + return val, nil } func (c *msgCache) SetSendMsgStatus(ctx context.Context, id string, status int32) error { @@ -720,7 +739,11 @@ func (c *msgCache) SetFcmToken(ctx context.Context, account string, platformID i } func (c *msgCache) GetFcmToken(ctx context.Context, account string, platformID int) (string, error) { - return utils.Wrap2(c.rdb.Get(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Result()) + val, err := c.rdb.Get(ctx, FCM_TOKEN+account+":"+strconv.Itoa(platformID)).Result() + if err != nil { + return "", errs.Wrap(err) + } + return val, nil } func (c *msgCache) DelFcmToken(ctx context.Context, account string, platformID int) error { @@ -738,7 +761,8 @@ func (c *msgCache) SetUserBadgeUnreadCountSum(ctx context.Context, userID string } func (c *msgCache) GetUserBadgeUnreadCountSum(ctx context.Context, userID string) (int, error) { - return utils.Wrap2(c.rdb.Get(ctx, userBadgeUnreadCountSum+userID).Int()) + val, err := c.rdb.Get(ctx, userBadgeUnreadCountSum+userID).Int() + return val, errs.Wrap(err) } func (c *msgCache) LockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error { @@ -771,42 +795,31 @@ func (c *msgCache) getMessageReactionExPrefix(clientMsgID string, sessionType in func (c *msgCache) JudgeMessageReactionExist(ctx context.Context, clientMsgID string, sessionType int32) (bool, error) { n, err := c.rdb.Exists(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType)).Result() if err != nil { - return false, utils.Wrap(err, "") + return false, errs.Wrap(err) } return n > 0, nil } -func (c *msgCache) SetMessageTypeKeyValue( - ctx context.Context, - clientMsgID string, - sessionType int32, - typeKey, value string, -) error { +func (c *msgCache) SetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey, value string) error { return errs.Wrap(c.rdb.HSet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey, value).Err()) } func (c *msgCache) SetMessageReactionExpire(ctx context.Context, clientMsgID string, sessionType int32, expiration time.Duration) (bool, error) { - return utils.Wrap2(c.rdb.Expire(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), expiration).Result()) + val, err := c.rdb.Expire(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), expiration).Result() + return val, errs.Wrap(err) } func (c *msgCache) GetMessageTypeKeyValue(ctx context.Context, clientMsgID string, sessionType int32, typeKey string) (string, error) { - return utils.Wrap2(c.rdb.HGet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey).Result()) + val, err := c.rdb.HGet(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), typeKey).Result() + return val, errs.Wrap(err) } -func (c *msgCache) GetOneMessageAllReactionList( - ctx context.Context, - clientMsgID string, - sessionType int32, -) (map[string]string, error) { - return utils.Wrap2(c.rdb.HGetAll(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType)).Result()) +func (c *msgCache) GetOneMessageAllReactionList(ctx context.Context, clientMsgID string, sessionType int32) (map[string]string, error) { + val, err := c.rdb.HGetAll(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType)).Result() + return val, errs.Wrap(err) } -func (c *msgCache) DeleteOneMessageKey( - ctx context.Context, - clientMsgID string, - sessionType int32, - subKey string, -) error { +func (c *msgCache) DeleteOneMessageKey(ctx context.Context, clientMsgID string, sessionType int32, subKey string) error { return errs.Wrap(c.rdb.HDel(ctx, c.getMessageReactionExPrefix(clientMsgID, sessionType), subKey).Err()) } diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 17b4a440d..fe1519867 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -16,6 +16,7 @@ package controller import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/v3/pkg/authverify" @@ -23,8 +24,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/tokenverify" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) @@ -78,7 +77,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(a.accessSecret)) if err != nil { - return "", utils.Wrap(err, "") + return "", errs.Wrap(err) } return tokenString, a.cache.AddTokenFlag(ctx, userID, platformID, tokenString, constant.NormalToken) } diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index b841a7d31..d427cc3a1 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -408,7 +408,7 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa log.ZError(ctx, "SetHasReadSeqs error", err2, "userSeqMap", userSeqMap, "conversationID", conversationID) prommetrics.SeqSetFailedCounter.Inc() } - return lastMaxSeq, isNew, utils.Wrap(err, "") + return lastMaxSeq, isNew, errs.Wrap(err) } func (db *commonMsgDatabase) getMsgBySeqs(ctx context.Context, userID, conversationID string, seqs []int64) (totalMsgs []*sdkws.MsgData, err error) { diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 9b461dd1f..0aa9fa58d 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -35,8 +35,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/utils" - table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) @@ -79,7 +77,7 @@ func (m *MsgMongoDriver) UpdateMsg( update := bson.M{"$set": bson.M{field: value}} res, err := m.MsgCollection.UpdateOne(ctx, filter, update) if err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } return res, nil } @@ -106,7 +104,7 @@ func (m *MsgMongoDriver) PushUnique( } res, err := m.MsgCollection.UpdateOne(ctx, filter, update) if err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } return res, nil } @@ -118,7 +116,7 @@ func (m *MsgMongoDriver) UpdateMsgContent(ctx context.Context, docID string, ind bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", index): msg}}, ) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } return nil } @@ -133,7 +131,7 @@ func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc( msg.Status = status bytes, err := proto.Marshal(msg) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } _, err = m.MsgCollection.UpdateOne( ctx, @@ -141,7 +139,7 @@ func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc( bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}}, ) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } return nil } @@ -167,12 +165,12 @@ func (m *MsgMongoDriver) GetMsgDocModelByIndex( findOpts, ) if err != nil { - return nil, utils.Wrap(err, "") + return nil, errs.Wrap(err) } var msgs []table.MsgDocModel err = cursor.All(ctx, &msgs) if err != nil { - return nil, utils.Wrap(err, fmt.Sprintf("cursor is %s", cursor.Current.String())) + return nil, errs.Wrap(err, fmt.Sprintf("cursor is %s", cursor.Current.String())) } if len(msgs) > 0 { return &msgs[0], nil @@ -223,7 +221,7 @@ func (m *MsgMongoDriver) DeleteMsgsInOneDocByIndex(ctx context.Context, docID st } _, err := m.MsgCollection.UpdateMany(ctx, bson.M{"doc_id": docID}, updates) if err != nil { - return utils.Wrap(err, "") + return errs.Wrap(err) } return nil } diff --git a/pkg/common/db/unrelation/user.go b/pkg/common/db/unrelation/user.go index 4b4a78c79..f5595c4eb 100644 --- a/pkg/common/db/unrelation/user.go +++ b/pkg/common/db/unrelation/user.go @@ -18,7 +18,6 @@ import ( "context" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/utils" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -119,7 +118,7 @@ func (u *UserMongoDriver) AddSubscriptionList(ctx context.Context, userID string opts, ) if err != nil { - return utils.Wrap(err, "transaction failed") + return errs.Wrap(err, "transaction failed") } } return nil diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go index a80d1c9a4..7fc456a1d 100644 --- a/pkg/common/http/http_client.go +++ b/pkg/common/http/http_client.go @@ -106,31 +106,31 @@ func PostReturn(ctx context.Context, url string, header map[string]string, input } func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error { - defer log.ZDebug(ctx, "callback", "url", url, "command", command, "input", input, "output", output, "callbackConfig", callbackConfig) - // - //v := urllib.Values{} - //v.Set(constant.CallbackCommand, command) - //url = url + "/" + v.Encode() url = url + "/" + command + log.ZInfo(ctx, "callback", "url", url, "input", input, "config", callbackConfig) b, err := Post(ctx, url, nil, input, callbackConfig.CallbackTimeOut) if err != nil { if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue { - log.ZWarn(ctx, "callback failed but continue", err, "url", url) + log.ZInfo(ctx, "callback failed but continue", err, "url", url) return nil } + log.ZWarn(ctx, "callback network failed", err, "url", url, "input", input) return errs.ErrNetwork.Wrap(err.Error()) } - defer log.ZDebug(ctx, "callback", "data", string(b)) - if err = json.Unmarshal(b, output); err != nil { if callbackConfig.CallbackFailedContinue != nil && *callbackConfig.CallbackFailedContinue { log.ZWarn(ctx, "callback failed but continue", err, "url", url) return nil } + log.ZWarn(ctx, "callback json unmarshal failed", err, "url", url, "input", input, "response", string(b)) return errs.ErrData.WithDetail(err.Error() + "response format error") } - - return output.Parse() + if err := output.Parse(); err != nil { + log.ZWarn(ctx, "callback parse failed", err, "url", url, "input", input, "response", string(b)) + return err + } + log.ZInfo(ctx, "callback success", "url", url, "input", input, "response", string(b)) + return nil } func CallBackPostReturn(ctx context.Context, url string, req callbackstruct.CallbackReq, resp callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error { diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 417aadb54..c2e0f33dc 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -27,7 +27,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "github.com/OpenIMSDK/tools/utils" "google.golang.org/protobuf/proto" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -153,10 +152,10 @@ func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Messag // Marshal the protobuf message bMsg, err := proto.Marshal(msg) if err != nil { - return 0, 0, utils.Wrap(err, "kafka proto Marshal err") + return 0, 0, errs.Wrap(err, "kafka proto Marshal err") } if len(bMsg) == 0 { - return 0, 0, utils.Wrap(errEmptyMsg, "") + return 0, 0, errs.Wrap(errEmptyMsg, "") } // Prepare Kafka message @@ -168,13 +167,13 @@ func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Messag // Validate message key and value if kMsg.Key.Length() == 0 || kMsg.Value.Length() == 0 { - return 0, 0, utils.Wrap(errEmptyMsg, "") + return 0, 0, errs.Wrap(errEmptyMsg) } // Attach context metadata as headers header, err := GetMQHeaderWithContext(ctx) if err != nil { - return 0, 0, utils.Wrap(err, "") + return 0, 0, errs.Wrap(err) } kMsg.Headers = header @@ -182,7 +181,7 @@ func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Messag partition, offset, err := p.producer.SendMessage(kMsg) if err != nil { log.ZWarn(ctx, "p.producer.SendMessage error", err) - return 0, 0, utils.Wrap(err, "") + return 0, 0, errs.Wrap(err) } log.ZDebug(ctx, "ByteEncoder SendMessage end", "key", kMsg.Key, "key length", kMsg.Value.Length()) From 3c738c3b28af4f0380244b76048a8e9e24ba7f91 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Wed, 21 Feb 2024 11:30:48 +0800 Subject: [PATCH 42/74] cicd: bump League Patch (#1937) --- internal/msggateway/client.go | 3 ++- internal/msggateway/compressor.go | 3 ++- internal/msggateway/encoder.go | 1 + internal/rpc/auth/auth.go | 1 + pkg/common/db/controller/auth.go | 2 ++ pkg/common/db/unrelation/msg.go | 1 + 6 files changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index dfd2b03d3..d3b0e6a5f 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -18,11 +18,12 @@ import ( "context" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "runtime/debug" "sync" "sync/atomic" + "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "google.golang.org/protobuf/proto" diff --git a/internal/msggateway/compressor.go b/internal/msggateway/compressor.go index 9bbec1ec9..d4789536e 100644 --- a/internal/msggateway/compressor.go +++ b/internal/msggateway/compressor.go @@ -18,9 +18,10 @@ import ( "bytes" "compress/gzip" "errors" - "github.com/OpenIMSDK/tools/errs" "io" "sync" + + "github.com/OpenIMSDK/tools/errs" ) var ( diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go index 2c46a774b..69a899591 100644 --- a/internal/msggateway/encoder.go +++ b/internal/msggateway/encoder.go @@ -17,6 +17,7 @@ package msggateway import ( "bytes" "encoding/gob" + "github.com/OpenIMSDK/tools/errs" ) diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 1b10ba06a..cde1e6ac5 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -29,6 +29,7 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/tokenverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index fe1519867..163a25c1c 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -16,6 +16,7 @@ package controller import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/v3/pkg/authverify" @@ -24,6 +25,7 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/tokenverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 0aa9fa58d..1c0686d28 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -35,6 +35,7 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" + table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) From 6a5a2dcc17cfb8e2bc01bf68b72d796dda3fd8b0 Mon Sep 17 00:00:00 2001 From: cui fliter Date: Wed, 21 Feb 2024 20:41:15 +0800 Subject: [PATCH 43/74] fix function name on comment (#1940) Signed-off-by: cui fliter --- tools/formitychecker/checker/checker.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/formitychecker/checker/checker.go b/tools/formitychecker/checker/checker.go index 7a1643358..c9ad8239f 100644 --- a/tools/formitychecker/checker/checker.go +++ b/tools/formitychecker/checker/checker.go @@ -29,7 +29,7 @@ var ( hyphenRegex = regexp.MustCompile(`^[a-zA-Z0-9\-]+\.[a-zA-Z0-9]+$`) ) -// CheckDirectoCheckDirectoryries initiates the checking process for the specified directories using configuration from config.Config. +// CheckDirectory initiates the checking process for the specified directories using configuration from config.Config. func CheckDirectory(cfg *config.Config) error { ignoreMap := make(map[string]struct{}) for _, dir := range cfg.IgnoreDirs { From 3c1d096e995fefc0ffde6cb4d7dd8399ade2c24c Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:57:22 +0800 Subject: [PATCH 44/74] docs:modify_README_Twitter (#1939) Signed-off-by: wxuanF <2569456943@qq.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d45c6b6fe..90848124f 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ + 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) + :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) + 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) -+ 💬 [Follow our Twitter account](https://twitter.com/openimsdk) ++ 💬 [Follow our Twitter account](https://twitter.com/founder_im63606) ## Ⓜ️ About OpenIM From b13f2614b880d6d0e73253514be882035cdfb3f5 Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Sun, 25 Feb 2024 22:23:29 +0800 Subject: [PATCH 45/74] makelint internal/ (#1951) Signed-off-by: xuan <146319162+wxuanF@users.noreply.github.com> --- internal/msggateway/client.go | 21 ++-- internal/msggateway/long_conn.go | 10 +- internal/msggateway/n_ws_server.go | 11 +- internal/msggateway/user_map.go | 4 +- internal/msgtransfer/init.go | 2 +- .../msgtransfer/online_history_msg_handler.go | 107 +++++++++--------- 6 files changed, 78 insertions(+), 77 deletions(-) diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index d3b0e6a5f..9a4005e6c 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -78,16 +78,17 @@ type Client struct { token string } -func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { - return &Client{ - w: new(sync.Mutex), - conn: conn, - PlatformID: utils.StringToInt(ctx.GetPlatformID()), - IsCompress: isCompress, - UserID: ctx.GetUserID(), - ctx: ctx, - } -} +// function not used +// func newClient(ctx *UserConnContext, conn LongConn, isCompress bool) *Client { +// return &Client{ +// w: new(sync.Mutex), +// conn: conn, +// PlatformID: utils.StringToInt(ctx.GetPlatformID()), +// IsCompress: isCompress, +// UserID: ctx.GetUserID(), +// ctx: ctx, +// } +// } // ResetClient updates the client's state with new connection and context information. func (c *Client) ResetClient( diff --git a/internal/msggateway/long_conn.go b/internal/msggateway/long_conn.go index 93e5cc33f..a4251a50f 100644 --- a/internal/msggateway/long_conn.go +++ b/internal/msggateway/long_conn.go @@ -108,10 +108,12 @@ func (d *GWebSocket) Dial(urlStr string, requestHeader http.Header) (*http.Respo } func (d *GWebSocket) IsNil() bool { - if d.conn != nil { - return false - } - return true + return d.conn == nil + // + // if d.conn != nil { + // return false + // } + // return true } func (d *GWebSocket) SetConnNil() { diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index c16da7c64..b734dee6d 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -61,11 +61,12 @@ type LongConnServer interface { MessageHandler } -var bufferPool = sync.Pool{ - New: func() any { - return make([]byte, 1024) - }, -} +// bufferPool is unused +// var bufferPool = sync.Pool{ +// New: func() any { +// return make([]byte, 1024) +// }, +// } type WsServer struct { port int diff --git a/internal/msggateway/user_map.go b/internal/msggateway/user_map.go index 052d7de2d..b4cec59fa 100644 --- a/internal/msggateway/user_map.go +++ b/internal/msggateway/user_map.go @@ -58,12 +58,12 @@ func (u *UserMap) Get(key string, platformID int) ([]*Client, bool, bool) { func (u *UserMap) Set(key string, v *Client) { allClients, existed := u.m.Load(key) if existed { - log.ZDebug(context.Background(), "Set existed", "user_id", key, "client", *v) + log.ZDebug(context.Background(), "Set existed", "user_id", key, "client_user_id", v.UserID) oldClients := allClients.([]*Client) oldClients = append(oldClients, v) u.m.Store(key, oldClients) } else { - log.ZDebug(context.Background(), "Set not existed", "user_id", key, "client", *v) + log.ZDebug(context.Background(), "Set not existed", "user_id", key, "client_user_id", v.UserID) var clients []*Client clients = append(clients, v) u.m.Store(key, clients) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 16d8613db..8ef3efd83 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -71,7 +71,7 @@ func StartTransfer(prometheusPort int) error { return err } - if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { + if err2 := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err2 != nil { return err } client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 6678715d4..6f0ee7706 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -74,10 +74,10 @@ type OnlineHistoryRedisConsumerHandler struct { chArrays [ChannelNum]chan Cmd2Value msgDistributionCh chan Cmd2Value - singleMsgSuccessCount uint64 - singleMsgFailedCount uint64 - singleMsgSuccessCountMutex sync.Mutex - singleMsgFailedCountMutex sync.Mutex + // singleMsgSuccessCount uint64 + // singleMsgFailedCount uint64 + // singleMsgSuccessCountMutex sync.Mutex + // singleMsgFailedCountMutex sync.Mutex msgDatabase controller.CommonMsgDatabase conversationRpcClient *rpcclient.ConversationRpcClient @@ -111,62 +111,59 @@ func NewOnlineHistoryRedisConsumerHandler( } func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) { - for { - select { - case cmd := <-och.chArrays[channelID]: - switch cmd.Cmd { - case SourceMessages: - msgChannelValue := cmd.Value.(MsgChannelValue) - ctxMsgList := msgChannelValue.ctxMsgList - ctx := msgChannelValue.ctx - log.ZDebug( + for cmd := range och.chArrays[channelID] { + switch cmd.Cmd { + case SourceMessages: + msgChannelValue := cmd.Value.(MsgChannelValue) + ctxMsgList := msgChannelValue.ctxMsgList + ctx := msgChannelValue.ctx + log.ZDebug( + ctx, + "msg arrived channel", + "channel id", + channelID, + "msgList length", + len(ctxMsgList), + "uniqueKey", + msgChannelValue.uniqueKey, + ) + storageMsgList, notStorageMsgList, storageNotificationList, notStorageNotificationList, modifyMsgList := och.getPushStorageMsgList( + ctxMsgList, + ) + log.ZDebug( + ctx, + "msg lens", + "storageMsgList", + len(storageMsgList), + "notStorageMsgList", + len(notStorageMsgList), + "storageNotificationList", + len(storageNotificationList), + "notStorageNotificationList", + len(notStorageNotificationList), + "modifyMsgList", + len(modifyMsgList), + ) + conversationIDMsg := msgprocessor.GetChatConversationIDByMsg(ctxMsgList[0].message) + conversationIDNotification := msgprocessor.GetNotificationConversationIDByMsg(ctxMsgList[0].message) + och.handleMsg(ctx, msgChannelValue.uniqueKey, conversationIDMsg, storageMsgList, notStorageMsgList) + och.handleNotification( + ctx, + msgChannelValue.uniqueKey, + conversationIDNotification, + storageNotificationList, + notStorageNotificationList, + ) + if err := och.msgDatabase.MsgToModifyMQ(ctx, msgChannelValue.uniqueKey, conversationIDNotification, modifyMsgList); err != nil { + log.ZError( ctx, - "msg arrived channel", - "channel id", - channelID, - "msgList length", - len(ctxMsgList), + "msg to modify mq error", + err, "uniqueKey", msgChannelValue.uniqueKey, - ) - storageMsgList, notStorageMsgList, storageNotificationList, notStorageNotificationList, modifyMsgList := och.getPushStorageMsgList( - ctxMsgList, - ) - log.ZDebug( - ctx, - "msg lens", - "storageMsgList", - len(storageMsgList), - "notStorageMsgList", - len(notStorageMsgList), - "storageNotificationList", - len(storageNotificationList), - "notStorageNotificationList", - len(notStorageNotificationList), "modifyMsgList", - len(modifyMsgList), + modifyMsgList, ) - conversationIDMsg := msgprocessor.GetChatConversationIDByMsg(ctxMsgList[0].message) - conversationIDNotification := msgprocessor.GetNotificationConversationIDByMsg(ctxMsgList[0].message) - och.handleMsg(ctx, msgChannelValue.uniqueKey, conversationIDMsg, storageMsgList, notStorageMsgList) - och.handleNotification( - ctx, - msgChannelValue.uniqueKey, - conversationIDNotification, - storageNotificationList, - notStorageNotificationList, - ) - if err := och.msgDatabase.MsgToModifyMQ(ctx, msgChannelValue.uniqueKey, conversationIDNotification, modifyMsgList); err != nil { - log.ZError( - ctx, - "msg to modify mq error", - err, - "uniqueKey", - msgChannelValue.uniqueKey, - "modifyMsgList", - modifyMsgList, - ) - } } } } From ae6d21e4ea523a386aa081131accf22a3ee2df90 Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Sun, 25 Feb 2024 23:01:22 +0800 Subject: [PATCH 46/74] docs:update README Community (#1943) Signed-off-by: wxuanF <2569456943@qq.com> --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 90848124f..e83afe5aa 100644 --- a/README.md +++ b/README.md @@ -53,12 +53,12 @@ ## :busts_in_silhouette: Community -+ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community) -+ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) ++ 💬 [Follow our Twitter account](https://twitter.com/founder_im63606) ++ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) + 🚀 [Join our Slack community](https://join.slack.com/t/openimsdk/shared_invite/zt-22720d66b-o_FvKxMTGXtcnnnHiMqe9Q) + :eyes: [Join our wechat (微信群)](https://openim-1253691595.cos.ap-nanjing.myqcloud.com/WechatIMG20.jpeg) -+ 👫 [Join our Reddit](https://www.reddit.com/r/OpenIMessaging) -+ 💬 [Follow our Twitter account](https://twitter.com/founder_im63606) ++ 📚 [OpenIM Community](https://github.com/OpenIMSDK/community) ++ 💕 [OpenIM Interest Group](https://github.com/Openim-sigs) ## Ⓜ️ About OpenIM From c8eed847113dc3badae45e77656b5641f246762f Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Mon, 26 Feb 2024 10:55:36 +0800 Subject: [PATCH 47/74] Fix lint errors in modified code (#1952) * pkg:makelint-#779 Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> * Update msg_utils.go * Update msg_utils.go * Update msg_utils.go * Update msg_utils.go * Update msg_utils.go * Update msg_utils.go * Update minio.go --------- Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- pkg/common/cmd/msg_utils.go | 18 ++++----- pkg/common/convert/friend.go | 12 +++++- pkg/common/db/cache/conversation.go | 49 ++++++++++++------------ pkg/common/db/controller/auth.go | 1 + pkg/common/db/controller/conversation.go | 2 +- pkg/common/db/controller/msg.go | 2 +- pkg/common/db/s3/cont/controller.go | 2 +- pkg/common/db/s3/cos/cos.go | 4 +- pkg/common/db/s3/minio/minio.go | 2 +- 9 files changed, 50 insertions(+), 42 deletions(-) diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index cfaf631ec..8c5ffb16c 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -22,7 +22,6 @@ import ( type MsgUtilsCmd struct { cobra.Command - msgTool *tools.MsgTool } func (m *MsgUtilsCmd) AddUserIDFlag() { @@ -38,19 +37,19 @@ func (m *MsgUtilsCmd) AddFixAllFlag() { m.Command.PersistentFlags().BoolP("fixAll", "f", false, "openIM fix all seqs") } -func (m *MsgUtilsCmd) getFixAllFlag(cmdLines *cobra.Command) bool { +/* func (m *MsgUtilsCmd) getFixAllFlag(cmdLines *cobra.Command) bool { fixAll, _ := cmdLines.Flags().GetBool("fixAll") return fixAll -} +} */ func (m *MsgUtilsCmd) AddClearAllFlag() { m.Command.PersistentFlags().BoolP("clearAll", "c", false, "openIM clear all seqs") } -func (m *MsgUtilsCmd) getClearAllFlag(cmdLines *cobra.Command) bool { +/* func (m *MsgUtilsCmd) getClearAllFlag(cmdLines *cobra.Command) bool { clearAll, _ := cmdLines.Flags().GetBool("clearAll") return clearAll -} +} */ func (m *MsgUtilsCmd) AddSuperGroupIDFlag() { m.Command.PersistentFlags().StringP("superGroupID", "g", "", "openIM superGroupID") @@ -65,19 +64,19 @@ func (m *MsgUtilsCmd) AddBeginSeqFlag() { m.Command.PersistentFlags().Int64P("beginSeq", "b", 0, "openIM beginSeq") } -func (m *MsgUtilsCmd) getBeginSeqFlag(cmdLines *cobra.Command) int64 { +/* func (m *MsgUtilsCmd) getBeginSeqFlag(cmdLines *cobra.Command) int64 { beginSeq, _ := cmdLines.Flags().GetInt64("beginSeq") return beginSeq -} +} */ func (m *MsgUtilsCmd) AddLimitFlag() { m.Command.PersistentFlags().Int64P("limit", "l", 0, "openIM limit") } -func (m *MsgUtilsCmd) getLimitFlag(cmdLines *cobra.Command) int64 { +/* func (m *MsgUtilsCmd) getLimitFlag(cmdLines *cobra.Command) int64 { limit, _ := cmdLines.Flags().GetInt64("limit") return limit -} +} */ func (m *MsgUtilsCmd) Execute() error { return m.Command.Execute() @@ -134,6 +133,7 @@ func NewSeqCmd() *SeqCmd { return seqCmd } + func (s *SeqCmd) GetSeqCmd() *cobra.Command { s.Command.Run = func(cmdLines *cobra.Command, args []string) { _, err := tools.InitMsgTool() diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index 27bd595ad..f3a19e459 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -26,7 +26,10 @@ import ( func FriendPb2DB(friend *sdkws.FriendInfo) *relation.FriendModel { dbFriend := &relation.FriendModel{} - utils.CopyStructFields(dbFriend, friend) + err := utils.CopyStructFields(dbFriend, friend) + if err != nil { + return nil + } dbFriend.FriendUserID = friend.FriendUser.UserID dbFriend.CreateTime = utils.UnixSecondToTime(friend.CreateTime) return dbFriend @@ -69,7 +72,11 @@ func FriendsDB2Pb( } for _, friend := range friendsDB { friendPb := &sdkws.FriendInfo{FriendUser: &sdkws.UserInfo{}} - utils.CopyStructFields(friendPb, friend) + err := utils.CopyStructFields(friendPb, friend) + if err != nil { + return nil, err + } + friendPb.FriendUser.UserID = users[friend.FriendUserID].UserID friendPb.FriendUser.Nickname = users[friend.FriendUserID].Nickname friendPb.FriendUser.FaceURL = users[friend.FriendUserID].FaceURL @@ -79,6 +86,7 @@ func FriendsDB2Pb( friendsPb = append(friendsPb, friendPb) } return friendsPb, nil + } func FriendRequestDB2Pb( diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index a7018bc18..047188947 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -16,7 +16,6 @@ package cache import ( "context" - "errors" "math/big" "strings" "time" @@ -220,16 +219,16 @@ func (c *ConversationRedisCache) DelConversations(ownerUserID string, conversati return cache } -func (c *ConversationRedisCache) getConversationIndex(convsation *relationtb.ConversationModel, keys []string) (int, error) { - key := c.getConversationKey(convsation.OwnerUserID, convsation.ConversationID) - for _i, _key := range keys { - if _key == key { - return _i, nil - } - } +// func (c *ConversationRedisCache) getConversationIndex(convsation *relationtb.ConversationModel, keys []string) (int, error) { +// key := c.getConversationKey(convsation.OwnerUserID, convsation.ConversationID) +// for _i, _key := range keys { +// if _key == key { +// return _i, nil +// } +// } - return 0, errors.New("not found key:" + key + " in keys") -} +// return 0, errors.New("not found key:" + key + " in keys") +// } func (c *ConversationRedisCache) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationtb.ConversationModel, error) { //var keys []string @@ -333,7 +332,7 @@ func (c *ConversationRedisCache) DelSuperGroupRecvMsgNotNotifyUserIDsHash(groupI return cache } -func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID string, conversationIDs []string) (int, error) { +/* func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID string, conversationIDs []string) (int, error) { for _i, _conversationID := range conversationIDs { if _conversationID == conversationID { return _i, nil @@ -341,21 +340,21 @@ func (c *ConversationRedisCache) getUserAllHasReadSeqsIndex(conversationID strin } return 0, errors.New("not found key:" + conversationID + " in keys") -} +} */ -//func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) { -// conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) -// if err != nil { -// return nil, err -// } -// var keys []string -// for _, conversarionID := range conversationIDs { -// keys = append(keys, c.getConversationHasReadSeqKey(ownerUserID, conversarionID)) -// } -// return batchGetCacheMap(ctx, c.rcClient, keys, conversationIDs, c.expireTime, c.getUserAllHasReadSeqsIndex, func(ctx context.Context) (map[string]int64, error) { -// return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID) -// }) -//} +/* func (c *ConversationRedisCache) GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) { + conversationIDs, err := c.GetUserConversationIDs(ctx, ownerUserID) + if err != nil { + return nil, err + } + var keys []string + for _, conversarionID := range conversationIDs { + keys = append(keys, c.getConversationHasReadSeqKey(ownerUserID, conversarionID)) + } + return batchGetCacheMap(ctx, c.rcClient, keys, conversationIDs, c.expireTime, c.getUserAllHasReadSeqsIndex, func(ctx context.Context) (map[string]int64, error) { + return c.conversationDB.GetUserAllHasReadSeqs(ctx, ownerUserID) + }) +} */ func (c *ConversationRedisCache) DelUserAllHasReadSeqs(ownerUserID string, conversationIDs ...string) ConversationCache { cache := c.NewCache() diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 163a25c1c..d3d5f7da9 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -75,6 +75,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI return "", err } } + claims := tokenverify.BuildClaims(userID, platformID, a.accessExpire) token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(a.accessSecret)) diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index c6629e9c8..c53d4ab87 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -105,7 +105,7 @@ func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, now := time.Now() for _, v := range NotUserIDs { temp := new(relationtb.ConversationModel) - if err := utils.CopyStructFields(temp, conversation); err != nil { + if err = utils.CopyStructFields(temp, conversation); err != nil { return err } temp.OwnerUserID = v diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index d427cc3a1..7eac624a7 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -846,7 +846,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio } } if len(delMsgIndexs) > 0 { - if err := db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil { + if err = db.msgDocDatabase.DeleteMsgsInOneDocByIndex(ctx, msgDocModel.DocID, delMsgIndexs); err != nil { log.ZError(ctx, "deleteMsgRecursion DeleteMsgsInOneDocByIndex failed", err, "conversationID", conversationID, "index", index) } delStruct.minSeq = int64(msgDocModel.Msg[delMsgIndexs[len(delMsgIndexs)-1]].Msg.Seq) diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go index 82c27c1f2..2a66aeaf6 100644 --- a/pkg/common/db/s3/cont/controller.go +++ b/pkg/common/db/s3/cont/controller.go @@ -106,7 +106,7 @@ func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64 partNumber++ } if maxParts > 0 && partNumber > 0 && partNumber < maxParts { - return nil, errors.New(fmt.Sprintf("too many parts: %d", partNumber)) + return nil, fmt.Errorf("too many parts: %d", partNumber) } if info, err := c.StatObject(ctx, c.HashPath(hash)); err == nil { return nil, &HashAlreadyExistsError{Object: info} diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go index a82ffe670..b302f1de6 100644 --- a/pkg/common/db/s3/cos/cos.go +++ b/pkg/common/db/s3/cos/cos.go @@ -52,8 +52,8 @@ const ( const successCode = http.StatusOK const ( - videoSnapshotImagePng = "png" - videoSnapshotImageJpg = "jpg" +// videoSnapshotImagePng = "png" +// videoSnapshotImageJpg = "jpg" ) func NewCos() (s3.Interface, error) { diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index 5a615dcfd..53a2864ef 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -140,7 +140,7 @@ func (m *Minio) initMinio(ctx context.Context) error { return fmt.Errorf("check bucket exists error: %w", err) } if !exists { - if err := m.core.Client.MakeBucket(ctx, conf.Bucket, minio.MakeBucketOptions{}); err != nil { + if err = m.core.Client.MakeBucket(ctx, conf.Bucket, minio.MakeBucketOptions{}); err != nil { return fmt.Errorf("make bucket error: %w", err) } } From 4803c8f004869f570c3e51c61369cb67c409dfda Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:47:54 +0800 Subject: [PATCH 48/74] /pkg-make lint (#1956) Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> --- pkg/common/cmd/msg_utils.go | 1 - pkg/common/db/controller/auth.go | 2 +- pkg/common/db/s3/minio/minio.go | 2 +- pkg/common/db/s3/minio/thumbnail.go | 5 ++- pkg/common/db/s3/oss/oss.go | 4 +- pkg/common/db/unrelation/msg.go | 63 +++++++++++++---------------- pkg/rpcclient/notification/group.go | 4 +- pkg/rpcclient/third.go | 6 ++- pkg/statistics/statistics.go | 4 +- 9 files changed, 43 insertions(+), 48 deletions(-) diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index 8c5ffb16c..dd6d645d7 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -133,7 +133,6 @@ func NewSeqCmd() *SeqCmd { return seqCmd } - func (s *SeqCmd) GetSeqCmd() *cobra.Command { s.Command.Run = func(cmdLines *cobra.Command, args []string) { _, err := tools.InitMsgTool() diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index d3d5f7da9..18c64ad8f 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -70,7 +70,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI } } if len(deleteTokenKey) != 0 { - err := a.cache.DeleteTokenByUidPid(ctx, userID, platformID, deleteTokenKey) + err = a.cache.DeleteTokenByUidPid(ctx, userID, platformID, deleteTokenKey) if err != nil { return "", err } diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index 53a2864ef..81545ff1b 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -149,7 +149,7 @@ func (m *Minio) initMinio(ctx context.Context) error { `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject","s3:PutObject"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::%s/*"],"Sid": ""}]}`, conf.Bucket, ) - if err := m.core.Client.SetBucketPolicy(ctx, conf.Bucket, policy); err != nil { + if err = m.core.Client.SetBucketPolicy(ctx, conf.Bucket, policy); err != nil { return err } } diff --git a/pkg/common/db/s3/minio/thumbnail.go b/pkg/common/db/s3/minio/thumbnail.go index 49c376c9f..5dfdaee09 100644 --- a/pkg/common/db/s3/minio/thumbnail.go +++ b/pkg/common/db/s3/minio/thumbnail.go @@ -82,7 +82,8 @@ func (m *Minio) getImageThumbnailURL(ctx context.Context, name string, expire ti } key, err := m.cache.GetThumbnailKey(ctx, name, opt.Format, opt.Width, opt.Height, func(ctx context.Context) (string, error) { if img == nil { - reader, err := m.core.Client.GetObject(ctx, m.bucket, name, minio.GetObjectOptions{}) + var reader *minio.Object + reader, err = m.core.Client.GetObject(ctx, m.bucket, name, minio.GetObjectOptions{}) if err != nil { return "", err } @@ -103,7 +104,7 @@ func (m *Minio) getImageThumbnailURL(ctx context.Context, name string, expire ti err = gif.Encode(buf, thumbnail, nil) } cacheKey := filepath.Join(imageThumbnailPath, info.Etag, fmt.Sprintf("image_w%d_h%d.%s", opt.Width, opt.Height, opt.Format)) - if _, err := m.core.Client.PutObject(ctx, m.bucket, cacheKey, buf, int64(buf.Len()), minio.PutObjectOptions{}); err != nil { + if _, err = m.core.Client.PutObject(ctx, m.bucket, cacheKey, buf, int64(buf.Len()), minio.PutObjectOptions{}); err != nil { return "", err } return cacheKey, nil diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go index 0bba97ee7..98473b87f 100644 --- a/pkg/common/db/s3/oss/oss.go +++ b/pkg/common/db/s3/oss/oss.go @@ -52,10 +52,10 @@ const ( const successCode = http.StatusOK -const ( +/* const ( videoSnapshotImagePng = "png" videoSnapshotImageJpg = "jpg" -) +) */ func NewOSS() (s3.Interface, error) { conf := config.Config.Object.Oss diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 1c0686d28..bc9118a9a 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -246,47 +246,42 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( indexs = append(indexs, m.model.GetMsgIndex(seq)) } pipeline := mongo.Pipeline{ - { - {"$match", bson.D{ - {"doc_id", docID}, - }}, - }, - { - {"$project", bson.D{ - {"_id", 0}, - {"doc_id", 1}, - {"msgs", bson.D{ - {"$map", bson.D{ - {"input", indexs}, - {"as", "index"}, - {"in", bson.D{ - {"$let", bson.D{ - {"vars", bson.D{ - {"currentMsg", bson.D{ - {"$arrayElemAt", []string{"$msgs", "$$index"}}, - }}, + bson.D{{Key: "$match", Value: bson.D{ + {Key: "doc_id", Value: docID}, + }}}, + bson.D{{Key: "$project", Value: bson.D{ + {Key: "_id", Value: 0}, + {Key: "doc_id", Value: 1}, + {Key: "msgs", Value: bson.D{ + {Key: "$map", Value: bson.D{ + {Key: "input", Value: indexs}, + {Key: "as", Value: "index"}, + {Key: "in", Value: bson.D{ + {Key: "$let", Value: bson.D{ + {Key: "vars", Value: bson.D{ + {Key: "currentMsg", Value: bson.D{ + {Key: "$arrayElemAt", Value: bson.A{"$msgs", "$$index"}}, }}, - {"in", bson.D{ - {"$cond", bson.D{ - {"if", bson.D{ - {"$in", []string{userID, "$$currentMsg.del_list"}}, - }}, - {"then", nil}, - {"else", "$$currentMsg"}, + }}, + {Key: "in", Value: bson.D{ + {Key: "$cond", Value: bson.D{ + {Key: "if", Value: bson.D{ + {Key: "$in", Value: bson.A{userID, "$$currentMsg.del_list"}}, }}, + {Key: "then", Value: nil}, + {Key: "else", Value: "$$currentMsg"}, }}, }}, }}, }}, }}, }}, - }, - { - {"$project", bson.D{ - {"msgs.del_list", 0}, - }}, - }, + }}}, + bson.D{{Key: "$project", Value: bson.D{ + {Key: "msgs.del_list", Value: 0}, + }}}, } + cur, err := m.MsgCollection.Aggregate(ctx, pipeline) if err != nil { return nil, errs.Wrap(err) @@ -800,7 +795,7 @@ func (m *MsgMongoDriver) RangeUserSendCount( } defer cur.Close(ctx) var result []Result - if err := cur.All(ctx, &result); err != nil { + if err = cur.All(ctx, &result); err != nil { return 0, 0, nil, nil, errs.Wrap(err) } if len(result) == 0 { @@ -1049,7 +1044,7 @@ func (m *MsgMongoDriver) RangeGroupSendCount( } defer cur.Close(ctx) var result []Result - if err := cur.All(ctx, &result); err != nil { + if err = cur.All(ctx, &result); err != nil { return 0, 0, nil, nil, errs.Wrap(err) } if len(result) == 0 { diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 8c3719b2c..7f40326b7 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -410,7 +410,7 @@ func (g *GroupNotificationSender) GroupApplicationAcceptedNotification(ctx conte return err } tips := &sdkws.GroupApplicationAcceptedTips{Group: group, HandleMsg: req.HandledMsg} - if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { + if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } for _, userID := range append(userIDs, req.FromUserID) { @@ -443,7 +443,7 @@ func (g *GroupNotificationSender) GroupApplicationRejectedNotification(ctx conte return err } tips := &sdkws.GroupApplicationRejectedTips{Group: group, HandleMsg: req.HandledMsg} - if err := g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { + if err = g.fillOpUser(ctx, &tips.OpUser, tips.Group.GroupID); err != nil { return err } for _, userID := range append(userIDs, req.FromUserID) { diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index 73d874005..b3557bf83 100755 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -42,13 +42,15 @@ func NewThird(discov discoveryregistry.SvcDiscoveryRegistry) *Third { } client := third.NewThirdClient(conn) minioClient, err := minioInit() + if err != nil { + panic(err) + } return &Third{discov: discov, Client: client, conn: conn, MinioClient: minioClient} } func minioInit() (*minio.Client, error) { minioClient := &minio.Client{} - var initUrl string - initUrl = config.Config.Object.Minio.Endpoint + initUrl := config.Config.Object.Minio.Endpoint minioUrl, err := url.Parse(initUrl) if err != nil { return nil, err diff --git a/pkg/statistics/statistics.go b/pkg/statistics/statistics.go index de6d04fec..6dfc8155c 100644 --- a/pkg/statistics/statistics.go +++ b/pkg/statistics/statistics.go @@ -36,9 +36,7 @@ func (s *Statistics) output() { var timeIntervalNum uint64 for { sum = *s.AllCount - select { - case <-t.C: - } + <-t.C if *s.AllCount-sum <= 0 { intervalCount = 0 } else { From b6ded445108d70ca1b9fd32c963b9cbba7bdaad3 Mon Sep 17 00:00:00 2001 From: hanson-wen <419847644@qq.com> Date: Mon, 26 Feb 2024 22:47:01 +0800 Subject: [PATCH 49/74] add aws upload, but not complete, leave some todos (#1858) * add aws upload, but not complete, leave some todos * Update config.yaml * Update environment.sh * Update aws.go * Update aws.go * Update aws.go --------- Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- config/templates/config.yaml.template | 7 + deployments/templates/config.yaml | 7 + go.mod | 10 +- pkg/common/config/config.go | 8 + pkg/common/db/s3/aws/aws.go | 275 ++++++++++++++++++++++++++ scripts/install/environment.sh | 8 + 6 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 pkg/common/db/s3/aws/aws.go diff --git a/config/templates/config.yaml.template b/config/templates/config.yaml.template index 32ac14361..03413c595 100644 --- a/config/templates/config.yaml.template +++ b/config/templates/config.yaml.template @@ -153,6 +153,13 @@ object: accessKeySecret: '' sessionToken: '' publicRead: false + aws: + endpoint: "" + region: "" + bucket: "demo-9999999" + accessKeyID: '' + accessKeySecret: '' + publicRead: false ###################### RPC Port Configuration ###################### # RPC service ports diff --git a/deployments/templates/config.yaml b/deployments/templates/config.yaml index cc318adcd..0aa6e68d6 100644 --- a/deployments/templates/config.yaml +++ b/deployments/templates/config.yaml @@ -153,6 +153,13 @@ object: accessKeySecret: ${KODO_ACCESS_KEY_SECRET} sessionToken: ${KODO_SESSION_TOKEN} publicRead: ${KODO_PUBLIC_READ} + aws: + endpoint: "${AWS_ENDPOINT}" # This might not be necessary unless you're using a custom endpoint + region: "${AWS_REGION}" + bucket: "${AWS_BUCKET}" + accessKeyID: ${AWS_ACCESS_KEY_ID} + accessKeySecret: ${AWS_SECRET_ACCESS_KEY} + publicRead: ${AWS_PUBLIC_READ} ###################### RPC Port Configuration ###################### # RPC service ports diff --git a/go.mod b/go.mod index ac02683b4..681d91099 100644 --- a/go.mod +++ b/go.mod @@ -50,10 +50,11 @@ require ( cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/firestore v1.14.0 // indirect - cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/longrunning v0.5.4 // indirect - cloud.google.com/go/storage v1.36.0 // indirect + cloud.google.com/go/firestore v1.13.0 // indirect + cloud.google.com/go/iam v1.1.2 // indirect + cloud.google.com/go/longrunning v0.5.1 // indirect + cloud.google.com/go/storage v1.30.1 // indirect + github.com/aws/aws-sdk-go v1.49.21 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -94,6 +95,7 @@ require ( github.com/jinzhu/copier v0.3.5 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect github.com/klauspost/compress v1.17.4 // indirect diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 9696e9367..7ee55d876 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -164,6 +164,14 @@ type configStruct struct { SessionToken string `yaml:"sessionToken"` PublicRead bool `yaml:"publicRead"` } `yaml:"kodo"` + Aws struct { + Endpoint string `yaml:"endpoint"` + Region string `yaml:"region"` + Bucket string `yaml:"bucket"` + AccessKeyID string `yaml:"accessKeyID"` + AccessKeySecret string `yaml:"accessKeySecret"` + PublicRead bool `yaml:"publicRead"` + } `yaml:"aws"` } `yaml:"object"` RpcPort struct { diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go new file mode 100644 index 000000000..14fe0c069 --- /dev/null +++ b/pkg/common/db/s3/aws/aws.go @@ -0,0 +1,275 @@ +// Copyright © 2023 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// docURL: https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html + +package aws + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + sdk "github.com/aws/aws-sdk-go/service/s3" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" +) + +const ( + minPartSize int64 = 1024 * 1024 * 1 // 1MB + maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB + maxNumSize int64 = 10000 +) + +// const ( +// imagePng = "png" +// imageJpg = "jpg" +// imageJpeg = "jpeg" +// imageGif = "gif" +// imageWebp = "webp" +// ) + +// const successCode = http.StatusOK + +// const ( +// videoSnapshotImagePng = "png" +// videoSnapshotImageJpg = "jpg" +// ) + +func NewAWS() (s3.Interface, error) { + conf := config.Config.Object.Aws + credential := credentials.NewStaticCredentials( + conf.AccessKeyID, // accessKey + conf.AccessKeySecret, // secretKey + "") // sts的临时凭证 + + sess, err := session.NewSession(&aws.Config{ + Region: aws.String(conf.Region), // 桶所在的区域 + Credentials: credential, + }) + + if err != nil { + return nil, err + } + return &Aws{ + bucket: conf.Bucket, + client: sdk.New(sess), + credential: credential, + }, nil +} + +type Aws struct { + bucket string + client *sdk.S3 + credential *credentials.Credentials +} + +func (a *Aws) Engine() string { + return "aws" +} + +func (a *Aws) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) { + input := &sdk.CreateMultipartUploadInput{ + Bucket: aws.String(a.bucket), // TODO: To be verified whether it is required + Key: aws.String(name), + } + result, err := a.client.CreateMultipartUploadWithContext(ctx, input) + if err != nil { + return nil, err + } + return &s3.InitiateMultipartUploadResult{ + Bucket: *result.Bucket, + Key: *result.Key, + UploadID: *result.UploadId, + }, nil +} + +func (a *Aws) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) { + sdkParts := make([]*sdk.CompletedPart, len(parts)) + for i, part := range parts { + sdkParts[i] = &sdk.CompletedPart{ + ETag: aws.String(part.ETag), + PartNumber: aws.Int64(int64(part.PartNumber)), + } + } + input := &sdk.CompleteMultipartUploadInput{ + Bucket: aws.String(a.bucket), // TODO: To be verified whether it is required + Key: aws.String(name), + UploadId: aws.String(uploadID), + MultipartUpload: &sdk.CompletedMultipartUpload{ + Parts: sdkParts, + }, + } + result, err := a.client.CompleteMultipartUploadWithContext(ctx, input) + if err != nil { + return nil, err + } + return &s3.CompleteMultipartUploadResult{ + Location: *result.Location, + Bucket: *result.Bucket, + Key: *result.Key, + ETag: *result.ETag, + }, nil +} + +func (a *Aws) PartSize(ctx context.Context, size int64) (int64, error) { + if size <= 0 { + return 0, errors.New("size must be greater than 0") + } + if size > maxPartSize*maxNumSize { + return 0, fmt.Errorf("AWS size must be less than the maximum allowed limit") + } + if size <= minPartSize*maxNumSize { + return minPartSize, nil + } + partSize := size / maxNumSize + if size%maxNumSize != 0 { + partSize++ + } + return partSize, nil +} + +func (a *Aws) DeleteObject(ctx context.Context, name string) error { + _, err := a.client.DeleteObjectWithContext(ctx, &sdk.DeleteObjectInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(name), + }) + return err +} + +func (a *Aws) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { + result, err := a.client.CopyObjectWithContext(ctx, &sdk.CopyObjectInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(dst), + CopySource: aws.String(src), + }) + if err != nil { + return nil, err + } + return &s3.CopyObjectInfo{ + ETag: *result.CopyObjectResult.ETag, + Key: dst, + }, nil +} + +func (a *Aws) IsNotFound(err error) bool { + if err == nil { + return false + } + if aerr, ok := err.(awserr.Error); ok { + switch aerr.Code() { + case sdk.ErrCodeNoSuchKey: + return true + default: + return false + } + } + return false +} + +func (a *Aws) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error { + _, err := a.client.AbortMultipartUploadWithContext(ctx, &sdk.AbortMultipartUploadInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(name), + UploadId: aws.String(uploadID), + }) + return err +} + +func (a *Aws) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) { + result, err := a.client.ListPartsWithContext(ctx, &sdk.ListPartsInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(name), + UploadId: aws.String(uploadID), + MaxParts: aws.Int64(int64(maxParts)), + PartNumberMarker: aws.Int64(int64(partNumberMarker)), + }) + if err != nil { + return nil, err + } + parts := make([]s3.UploadedPart, len(result.Parts)) + for i, part := range result.Parts { + parts[i] = s3.UploadedPart{ + PartNumber: int(*part.PartNumber), + LastModified: *part.LastModified, + Size: *part.Size, + ETag: *part.ETag, + } + } + return &s3.ListUploadedPartsResult{ + Key: *result.Key, + UploadID: *result.UploadId, + NextPartNumberMarker: int(*result.NextPartNumberMarker), + MaxParts: int(*result.MaxParts), + UploadedParts: parts, + }, nil +} + +func (a *Aws) PartLimit() *s3.PartLimit { + return &s3.PartLimit{ + MinPartSize: minPartSize, + MaxPartSize: maxPartSize, + MaxNumSize: maxNumSize, + } +} + +func (a *Aws) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) { + req, _ := a.client.PutObjectRequest(&sdk.PutObjectInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(name), + }) + url, err := req.Presign(expire) + if err != nil { + return "", err + } + return url, nil +} + +func (a *Aws) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { + result, err := a.client.GetObjectWithContext(ctx, &sdk.GetObjectInput{ + Bucket: aws.String(a.bucket), + Key: aws.String(name), + }) + if err != nil { + return nil, err + } + res := &s3.ObjectInfo{ + Key: name, + ETag: *result.ETag, + Size: *result.ContentLength, + LastModified: *result.LastModified, + } + return res, nil +} + +// AccessURL todo +func (a *Aws) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { + // todo + return "", nil +} + +func (a *Aws) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { + // todo + return nil, nil +} + +func (a *Aws) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) { + // todo + return nil, nil +} diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index b1d2354b9..896288775 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -222,6 +222,14 @@ def "KODO_ACCESS_KEY_SECRET" # 七 def "KODO_SESSION_TOKEN" # 七牛云OSS的会话令牌 def "KODO_PUBLIC_READ" "false" # 公有读 +# AWS Configuration Information +def "AWS_ENDPOINT" "" # AWS endpoint, generally not needed unless using a specific service +def "AWS_REGION" "us-east-1" # AWS Region +def "AWS_BUCKET" "demo-9999999" # AWS S3 Bucket Name +def "AWS_ACCESS_KEY_ID" # AWS Access Key ID +def "AWS_SECRET_ACCESS_KEY" # AWS Secret Access Key +def "AWS_PUBLIC_READ" "false" # Public read access + ###################### Redis 配置信息 ###################### def "REDIS_PORT" "16379" # Redis的端口 def "REDIS_ADDRESS" "${DOCKER_BRIDGE_GATEWAY}" # Redis的地址 From 6c4334b7305fc376ae461f0b58bfb209c0becb60 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Tue, 27 Feb 2024 14:19:21 +0800 Subject: [PATCH 50/74] cicd: bump League Patch (#1958) --- go.mod | 10 +++++----- go.sum | 7 +++++++ pkg/common/db/s3/aws/aws.go | 1 + 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 681d91099..30bfcf146 100644 --- a/go.mod +++ b/go.mod @@ -36,6 +36,7 @@ require github.com/google/uuid v1.6.0 require ( github.com/IBM/sarama v1.42.2 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible + github.com/aws/aws-sdk-go v1.49.21 github.com/go-redis/redis v6.15.9+incompatible github.com/redis/go-redis/v9 v9.4.0 github.com/spf13/pflag v1.0.5 @@ -50,11 +51,10 @@ require ( cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/firestore v1.13.0 // indirect - cloud.google.com/go/iam v1.1.2 // indirect - cloud.google.com/go/longrunning v0.5.1 // indirect - cloud.google.com/go/storage v1.30.1 // indirect - github.com/aws/aws-sdk-go v1.49.21 // indirect + cloud.google.com/go/firestore v1.14.0 // indirect + cloud.google.com/go/iam v1.1.5 // indirect + cloud.google.com/go/longrunning v0.5.4 // indirect + cloud.google.com/go/storage v1.36.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/go.sum b/go.sum index 38596d45e..6277afac3 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go v1.49.21 h1:Rl8KW6HqkwzhATwvXhyr7vD4JFUMi7oXGAw9SrxxIFY= +github.com/aws/aws-sdk-go v1.49.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -203,6 +205,10 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -532,6 +538,7 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go index 14fe0c069..42d5cd14c 100644 --- a/pkg/common/db/s3/aws/aws.go +++ b/pkg/common/db/s3/aws/aws.go @@ -27,6 +27,7 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" sdk "github.com/aws/aws-sdk-go/service/s3" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) From 8e6c1d74c79846a9456cfcc5ab00dd1a51744d92 Mon Sep 17 00:00:00 2001 From: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:44:51 +0800 Subject: [PATCH 51/74] fix: change consumer group receive messages avoid kafka consumer group always rebalanced. (#1942) --- internal/msgtransfer/init.go | 9 ++------- internal/msgtransfer/online_history_msg_handler.go | 1 + internal/push/consumer_init.go | 7 +------ pkg/common/kafka/consumer_group.go | 13 ++++++------- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 8ef3efd83..1766a5419 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -20,8 +20,6 @@ import ( "fmt" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net/http" @@ -118,11 +116,8 @@ func (m *MsgTransfer) Start(prometheusPort int) error { netErr error ) - onError := func(ctx context.Context, err error, errInfo string) { - log.ZWarn(ctx, errInfo, err) - } - go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH, onError) - go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH, onError) + go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH) + go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH) if config.Config.Prometheus.Enable { go func() { diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 6f0ee7706..393ec7a75 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -438,6 +438,7 @@ func (och *OnlineHistoryRedisConsumerHandler) ConsumeClaim( wg = sync.WaitGroup{} running = new(atomic.Bool) ) + running.Store(true) wg.Add(1) go func() { diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index 572afe0eb..daaa37e8a 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -16,8 +16,6 @@ package push import ( "context" - - "github.com/OpenIMSDK/tools/log" ) type Consumer struct { @@ -36,9 +34,6 @@ func NewConsumer(pusher *Pusher) (*Consumer, error) { } func (c *Consumer) Start() { - onError := func(ctx context.Context, err error, errInfo string) { - log.ZWarn(ctx, errInfo, err) - } - go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh, onError) + go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh) } diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 5245c6a6f..d63527620 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,8 +17,6 @@ package kafka import ( "context" "errors" - "fmt" - "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" @@ -67,18 +65,19 @@ func (mc *MConsumerGroup) GetContextFromMsg(cMsg *sarama.ConsumerMessage) contex return GetContextWithMQHeader(cMsg.Headers) } -func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler, onError func(context.Context, error, string)) { +func (mc *MConsumerGroup) RegisterHandleAndConsumer(ctx context.Context, handler sarama.ConsumerGroupHandler) { log.ZDebug(ctx, "register consumer group", "groupID", mc.groupID) for { err := mc.ConsumerGroup.Consume(ctx, mc.topics, handler) - if errors.Is(err, sarama.ErrClosedConsumerGroup) || errors.Is(err, context.Canceled) { + if errors.Is(err, sarama.ErrClosedConsumerGroup) { return } - if err != nil { - errInfo := fmt.Sprintf("consume err: %v, topic: %v, groupID: %s", err, strings.Join(mc.topics, ", "), mc.groupID) - onError(ctx, err, errInfo) // 调用回调函数处理错误 + if errors.Is(err, context.Canceled) { return } + if err != nil { + log.ZWarn(ctx, "consume err", err, "topic", mc.topics, "groupID", mc.groupID) + } } } From ac8775827d1665ee8c0f67ebe9cc0181aab3cbcf Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:58:54 +0800 Subject: [PATCH 52/74] fix: reconstruct the script of 'make start' 'make stop' and 'make check' (#1953) * fix: del the error world * fix: refactoring scripts * fix: del nounset * rm set error * rm set error * replace openim::log::info * replace openim::log::info * replace openim::log::info * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * fix: fix the error output format * log * fix: fix the error * log * log * fix: fi the code error * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * log * fix: use printf replace the echo --------- Co-authored-by: skiffer-git <44203734@qq.com> --- cmd/openim-api/main.go | 2 +- install.sh | 2 +- internal/msggateway/init.go | 4 +- internal/msgtransfer/init.go | 6 +- pkg/common/startrpc/start.go | 4 +- pkg/util/genutil/genutil.go | 10 +- scripts/advertise.sh | 2 +- scripts/build-all-service.sh | 6 +- scripts/check-all.sh | 43 +++--- scripts/cherry-pick.sh | 6 +- scripts/common.sh | 6 +- scripts/docker-start-all.sh | 6 +- scripts/gen-swagger-docs.sh | 6 +- scripts/init-config.sh | 6 +- scripts/init-env.sh | 4 +- scripts/install-im-server.sh | 6 +- scripts/install/common.sh | 22 ++- scripts/install/dependency.sh | 6 +- scripts/install/install.sh | 2 +- scripts/install/openim-api.sh | 29 ++-- scripts/install/openim-crontask.sh | 8 +- scripts/install/openim-man.sh | 2 +- scripts/install/openim-msggateway.sh | 15 +- scripts/install/openim-msgtransfer.sh | 62 +++++--- scripts/install/openim-push.sh | 16 +-- scripts/install/openim-rpc.sh | 36 +++-- scripts/install/openim-tools.sh | 8 +- scripts/lib/golang.sh | 51 +++++++ scripts/lib/init.sh | 6 +- scripts/lib/logging.sh | 18 ++- scripts/lib/util.sh | 195 ++++++++++++++++++-------- scripts/list-feature-tests.sh | 6 +- scripts/release.sh | 6 +- scripts/run-in-gopath.sh | 6 +- scripts/start-all.sh | 100 ++++++++----- scripts/stop-all.sh | 39 ++++-- scripts/update-generated-docs.sh | 6 +- scripts/update-yamlfmt.sh | 6 +- scripts/verify-pkg-names.sh | 6 +- scripts/verify-shellcheck.sh | 6 +- scripts/verify-spelling.sh | 6 +- scripts/verify-typecheck.sh | 6 +- scripts/verify-yamlfmt.sh | 6 +- 43 files changed, 513 insertions(+), 281 deletions(-) diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 7a7e06293..34ada1d57 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -119,7 +119,7 @@ func run(port int, proPort int) error { defer cancel() select { case <-sigs: - util.SIGUSR1Exit() + util.SIGTERMExit() err := server.Shutdown(ctx) if err != nil { return errs.Wrap(err, "shutdown err") diff --git a/install.sh b/install.sh index 7ff0f8739..4b8e74c55 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ # set -e -set -o pipefail + ############################## OpenIM Github ############################## # ... rest of the script ... diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index 321407f7e..b18efcd50 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -46,9 +46,7 @@ func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error { netDone := make(chan error) go func() { err = hubServer.Start() - if err != nil { - netDone <- err - } + netDone <- err }() return hubServer.LongConnServer.Run(netDone) } diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 1766a5419..df06ec0f9 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -139,12 +139,12 @@ func (m *MsgTransfer) Start(prometheusPort int) error { signal.Notify(sigs, syscall.SIGTERM) select { case <-sigs: - util.SIGUSR1Exit() + util.SIGTERMExit() // graceful close kafka client. m.cancel() m.historyCH.historyConsumerGroup.Close() m.historyMongoCH.historyConsumerGroup.Close() - + return nil case <-netDone: m.cancel() m.historyCH.historyConsumerGroup.Close() @@ -152,6 +152,4 @@ func (m *MsgTransfer) Start(prometheusPort int) error { close(netDone) return netErr } - - return nil } diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index c5105ec51..be9fbd25b 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -140,7 +140,7 @@ func Start( signal.Notify(sigs, syscall.SIGTERM) select { case <-sigs: - util.SIGUSR1Exit() + util.SIGTERMExit() ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() if err := gracefulStopWithCtx(ctx, srv.GracefulStop); err != nil { @@ -152,7 +152,7 @@ func Start( if err != nil { return errs.Wrap(err, "shutdown err") } - return errors.New("SIGTERM EXIT") + return nil case <-netDone: close(netDone) return netErr diff --git a/pkg/util/genutil/genutil.go b/pkg/util/genutil/genutil.go index f97b803f6..bd9376d58 100644 --- a/pkg/util/genutil/genutil.go +++ b/pkg/util/genutil/genutil.go @@ -15,7 +15,6 @@ package genutil import ( - "errors" "fmt" "os" "path/filepath" @@ -42,15 +41,12 @@ func OutDir(path string) (string, error) { } func ExitWithError(err error) { - if errors.Is(err, errors.New("SIGTERM EXIT")) { - os.Exit(-1) - } progName := filepath.Base(os.Args[0]) - fmt.Fprintf(os.Stderr, "\n\n%s exit -1: \n%+v\n\n", progName, err) + fmt.Fprintf(os.Stderr, "%s exit -1: %+v\n", progName, err) os.Exit(-1) } -func SIGUSR1Exit() { +func SIGTERMExit() { progName := filepath.Base(os.Args[0]) - fmt.Printf("\n\n%s receive process terminal SIGTERM exit 0\n\n", progName) + fmt.Fprintf(os.Stderr, "Warning %s receive process terminal SIGTERM exit 0\n", progName) } diff --git a/scripts/advertise.sh b/scripts/advertise.sh index 3effc4f2b..958b4e3ae 100755 --- a/scripts/advertise.sh +++ b/scripts/advertise.sh @@ -14,7 +14,7 @@ # limitations under the License. set -e -set -o pipefail + . $(dirname ${BASH_SOURCE})/lib/init.sh diff --git a/scripts/build-all-service.sh b/scripts/build-all-service.sh index b5578fca6..6335b0e08 100755 --- a/scripts/build-all-service.sh +++ b/scripts/build-all-service.sh @@ -22,9 +22,9 @@ # Usage: `scripts/build-all-service.sh`. # Example: `scripts/build-go.sh WHAT=cmd/kubelet`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/check-all.sh b/scripts/check-all.sh index 062605ae1..a9b07d65b 100755 --- a/scripts/check-all.sh +++ b/scripts/check-all.sh @@ -19,9 +19,9 @@ # Encapsulated as: `make check`. # READ: https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/install/common.sh" @@ -49,13 +49,6 @@ print_services_and_ports() { echo "+-------------------------+----------+" } -handle_error() { - echo "An error occurred. Printing ${STDERR_LOG_FILE} contents:" - cat "${STDERR_LOG_FILE}" - exit 1 -} - -trap handle_error ERR # Assuming OPENIM_SERVER_NAME_TARGETS and OPENIM_SERVER_PORT_TARGETS are defined # Similarly for OPENIM_DEPENDENCY_TARGETS and OPENIM_DEPENDENCY_PORT_TARGETS @@ -71,7 +64,6 @@ echo "++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}" openim::log::info "\n## Check all dependent service ports" echo "++ The port being checked: ${OPENIM_DEPENDENCY_PORT_LISTARIES[@]}" -set +e # Later, after discarding Docker, the Docker keyword is unreliable, and Kubepods is used if grep -qE 'docker|kubepods' /proc/1/cgroup || [ -f /.dockerenv ]; then @@ -85,22 +77,29 @@ if [[ $? -ne 0 ]]; then openim::log::error_exit "The service does not start properly, please check the port, query variable definition!" echo "+++ https://github.com/openimsdk/open-im-server/tree/main/scripts/install/environment.sh +++" else - echo "++++ Check all dependent service ports successfully !" + openim::log::success "All components depended on by openim are running normally! " fi -openim::log::info "\n## Check OpenIM service name" -. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check -openim::log::info "\n## Check all OpenIM service ports" -echo "+++ The port being checked: ${OPENIM_SERVER_PORT_LISTARIES[@]}" -openim::util::check_ports ${OPENIM_SERVER_PORT_LISTARIES[@]} +openim::log::info "\n## Check openim service name:\n${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer" +result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check) if [[ $? -ne 0 ]]; then echo "+++ cat openim log file >>> ${LOG_FILE}" - openim::log::error_exit "The service does not start properly, please check the port, query variable definition!" -else - echo "++++ Check all openim service ports successfully !" + openim::log::error "check process failed.\n $result" fi -set -e -trap - ERR \ No newline at end of file +echo "Check openim service name:" +for item in "${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}"; do + echo "$item" +done + +result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}) +if [[ $? -ne 0 ]]; then + echo "+++ cat openim log file >>> ${LOG_FILE}" + openim::log::error "check process failed.\n " + echo "$result" +else + openim::log::success "All openim services are running normally! " +fi + diff --git a/scripts/cherry-pick.sh b/scripts/cherry-pick.sh index 8a1f8dd79..f8d7912f8 100755 --- a/scripts/cherry-pick.sh +++ b/scripts/cherry-pick.sh @@ -21,9 +21,9 @@ # checks them out to a branch named: # automated-cherry-pick-of--- -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/common.sh b/scripts/common.sh index d67389d56..702f55588 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -18,9 +18,9 @@ # shellcheck disable=SC2034 # Variables sourced in other scripts. # Common utilities, variables and checks for all build scripts. -set -o errexit -set +o nounset -set -o pipefail + + + # Unset CDPATH, having it set messes up with script import paths unset CDPATH diff --git a/scripts/docker-start-all.sh b/scripts/docker-start-all.sh index 162655553..c47069e3b 100755 --- a/scripts/docker-start-all.sh +++ b/scripts/docker-start-all.sh @@ -14,9 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -o errexit -set -o nounset -set -o pipefail + + + #fixme This scripts is the total startup scripts #fixme The full name of the shell scripts that needs to be started is placed in the need_to_start_server_shell array diff --git a/scripts/gen-swagger-docs.sh b/scripts/gen-swagger-docs.sh index 68410e79c..e768f9106 100755 --- a/scripts/gen-swagger-docs.sh +++ b/scripts/gen-swagger-docs.sh @@ -15,9 +15,9 @@ # Script to generate docs from the latest swagger spec. -set -o errexit -set -o nounset -set -o pipefail + + + # The root of the build/dist directory OPENIM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd -P)" diff --git a/scripts/init-config.sh b/scripts/init-config.sh index 82eefbb54..c7cf16320 100755 --- a/scripts/init-config.sh +++ b/scripts/init-config.sh @@ -15,9 +15,9 @@ # This script automatically initializes various configuration files and can generate example files. -set -o errexit -set -o nounset -set -o pipefail + + + # Root directory of the OpenIM project OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. diff --git a/scripts/init-env.sh b/scripts/init-env.sh index 75b871b08..1e802c3be 100755 --- a/scripts/init-env.sh +++ b/scripts/init-env.sh @@ -16,8 +16,8 @@ #FIXME This script is the startup script for multiple servers. #FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. -set -o nounset -set -o pipefail + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/.. && pwd -P) source "${OPENIM_ROOT}/scripts/install/common.sh" diff --git a/scripts/install-im-server.sh b/scripts/install-im-server.sh index c1224e30c..d11a49dc8 100755 --- a/scripts/install-im-server.sh +++ b/scripts/install-im-server.sh @@ -27,9 +27,9 @@ # Usage: # SERVER_IMAGE_VERSION=latest IMAGE_REGISTRY=myregistry ./this_script.sh -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/install/common.sh b/scripts/install/common.sh index f6ee5d3ad..ed1ba1a4b 100755 --- a/scripts/install/common.sh +++ b/scripts/install/common.sh @@ -15,9 +15,9 @@ # Common utilities, variables and checks for all build scripts. -set -o errexit -set +o nounset -set -o pipefail + + + # Sourced flag COMMON_SOURCED=true @@ -99,6 +99,22 @@ IFS=" " read -ra OPENIM_SERVER_PORT_TARGETS <<< "$(openim::common::service_port) readonly OPENIM_SERVER_PORT_TARGETS readonly OPENIM_SERVER_PORT_LISTARIES=("${OPENIM_SERVER_PORT_TARGETS[@]##*/}") + +OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER=() + +for target in "${OPENIM_SERVER_BINARIES_NO_TRANSFER[@]}"; do + OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER + + + +OPENIM_ALL_SERVICE_LIBRARIES=() +for target in "${OPENIM_SERVER_BINARIES_NO_CMDUTILS[@]}"; do + OPENIM_ALL_SERVICE_LIBRARIES+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_SERVICE_LIBRARIES + openim::common::dependency_name() { local targets=( redis diff --git a/scripts/install/dependency.sh b/scripts/install/dependency.sh index e7c7eb426..bad1cb6f9 100755 --- a/scripts/install/dependency.sh +++ b/scripts/install/dependency.sh @@ -15,9 +15,9 @@ # This script will install the dependencies required for openim -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh diff --git a/scripts/install/install.sh b/scripts/install/install.sh index d5ec5b7f7..bb09675bf 100755 --- a/scripts/install/install.sh +++ b/scripts/install/install.sh @@ -109,7 +109,7 @@ function openim::uninstall::uninstall_openim() { openim::common::sudo "systemctl stop openim.target" openim::common::sudo "systemctl disable openim.target" openim::common::sudo "rm -f /etc/systemd/system/openim.target" - set -o errexit + openim::log::success "openim uninstall success" } diff --git a/scripts/install/openim-api.sh b/scripts/install/openim-api.sh index be2a2d33b..8a7b8d1f1 100755 --- a/scripts/install/openim-api.sh +++ b/scripts/install/openim-api.sh @@ -14,9 +14,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh @@ -47,24 +47,31 @@ function openim::api::start() { openim::log::info "Starting ${SERVER_NAME} ..." + readonly OPENIM_API_SERVER_LIBRARIES="${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}" + + printf "+------------------------+--------------+\n" printf "| Service Name | Port |\n" printf "+------------------------+--------------+\n" - + + local length=${#OPENIM_API_SERVICE_LISTARIES[@]} - for ((i=0; i> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & - openim::util::check_process_names ${SERVER_NAME} + return 0 } @@ -107,7 +105,7 @@ function openim::crontask::uninstall() { openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" - set -o errexit + openim::log::info "uninstall ${SERVER_NAME} successfully" } diff --git a/scripts/install/openim-man.sh b/scripts/install/openim-man.sh index fac5cebea..642588a28 100755 --- a/scripts/install/openim-man.sh +++ b/scripts/install/openim-man.sh @@ -76,7 +76,7 @@ function openim::man::uninstall() { # Turn off exit-on-error temporarily to handle non-existing files gracefully set +o errexit openim::common::sudo "rm -f /usr/share/man/man1/openim-*" - set -o errexit + openim::log::info "Uninstalled openim man pages successfully" } diff --git a/scripts/install/openim-msggateway.sh b/scripts/install/openim-msggateway.sh index a2316c784..3eb9bb349 100755 --- a/scripts/install/openim-msggateway.sh +++ b/scripts/install/openim-msggateway.sh @@ -14,14 +14,13 @@ # limitations under the License. # Common utilities, variables and checks for all build scripts. -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh -openim::util::set_max_fd 200000 SERVER_NAME="openim-msggateway" @@ -31,8 +30,7 @@ function openim::msggateway::start() { openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGGATEWAY_BINARY}" - - openim::util::stop_services_with_name ${OPENIM_MSGGATEWAY_BINARY} + # OpenIM message gateway service port OPENIM_MESSAGE_GATEWAY_PORTS=$(openim::util::list-to-string ${OPENIM_MESSAGE_GATEWAY_PORT} ) @@ -66,8 +64,7 @@ function openim::msggateway::start() { nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done - - openim::util::check_process_names ${SERVER_NAME} + return 0 } ###################################### Linux Systemd ###################################### @@ -117,7 +114,7 @@ function openim::msggateway::uninstall() { openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" - set -o errexit + openim::log::info "uninstall ${SERVER_NAME} successfully" } diff --git a/scripts/install/openim-msgtransfer.sh b/scripts/install/openim-msgtransfer.sh index 783e06729..9fdf07fe2 100755 --- a/scripts/install/openim-msgtransfer.sh +++ b/scripts/install/openim-msgtransfer.sh @@ -16,15 +16,13 @@ # ./scripts/install/openim-msgtransfer.sh openim::msgtransfer::start # Common utilities, variables and checks for all build scripts. -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh -openim::util::set_max_fd 200000 - SERVER_NAME="openim-msgtransfer" function openim::msgtransfer::start() { @@ -33,9 +31,7 @@ function openim::msgtransfer::start() { openim::log::info "Start OpenIM Msggateway, binary root: ${SERVER_NAME}" openim::log::status "Start OpenIM Msggateway, path: ${OPENIM_MSGTRANSFER_BINARY}" - - openim::util::stop_services_with_name ${OPENIM_MSGTRANSFER_BINARY} - + # Message Transfer Prometheus port list MSG_TRANSFER_PROM_PORTS=(openim::util::list-to-string ${MSG_TRANSFER_PROM_PORT} ) @@ -46,11 +42,11 @@ function openim::msgtransfer::start() { openim::log::info "openim maggateway num: ${OPENIM_MSGGATEWAY_NUM}" if [ "${OPENIM_MSGGATEWAY_NUM}" -lt 1 ]; then - opeim::log::error_exit "OPENIM_MSGGATEWAY_NUM must be greater than 0" + opeim::log::error "OPENIM_MSGGATEWAY_NUM must be greater than 0" fi if [ ${OPENIM_MSGGATEWAY_NUM} -ne $((${#MSG_TRANSFER_PROM_PORTS[@]} - 1)) ]; then - openim::log::error_exit "OPENIM_MSGGATEWAY_NUM must be equal to the number of MSG_TRANSFER_PROM_PORTS" + openim::log::error "OPENIM_MSGGATEWAY_NUM must be equal to the number of MSG_TRANSFER_PROM_PORTS" fi for (( i=0; i<$OPENIM_MSGGATEWAY_NUM; i++ )) do @@ -63,17 +59,18 @@ function openim::msgtransfer::start() { fi nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done - - openim::util::check_process_names "${OPENIM_OUTPUT_HOSTBIN}/${SERVER_NAME}" + return 0 + } function openim::msgtransfer::check() { PIDS=$(pgrep -f "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer") - - NUM_PROCESSES=$(echo "$PIDS" | wc -l) - + if [ -z "$PIDS" ]; then + NUM_PROCESSES=0 + else + NUM_PROCESSES=$(echo "$PIDS" | wc -l) + fi if [ "$NUM_PROCESSES" -eq "$OPENIM_MSGGATEWAY_NUM" ]; then - openim::log::info "Found $OPENIM_MSGGATEWAY_NUM processes named $OPENIM_OUTPUT_HOSTBIN" for PID in $PIDS; do if [[ "$OSTYPE" == "linux-gnu"* ]]; then ps -p $PID -o pid,cmd @@ -84,10 +81,39 @@ function openim::msgtransfer::check() { fi done else - openim::log::error_exit "Expected $OPENIM_MSGGATEWAY_NUM openim msgtransfer processes, but found $NUM_PROCESSES msgtransfer processes." + openim::log::error "Expected $OPENIM_MSGGATEWAY_NUM openim msgtransfer processes, but found $NUM_PROCESSES msgtransfer processes." + return 1 fi + return 0 } +function openim::msgtransfer::check_for_stop() { + PIDS=$(pgrep -f "${OPENIM_OUTPUT_HOSTBIN}/openim-msgtransfer") || PIDS="0" + if [ "$PIDS" = "0" ]; then + return 0 + fi + + NUM_PROCESSES=$(echo "$PIDS" | wc -l | xargs) + + if [ "$NUM_PROCESSES" -gt 0 ]; then + openim::log::error "Found $NUM_PROCESSES processes for $OPENIM_OUTPUT_HOSTBIN/openim-msgtransfer" + for PID in $PIDS; do + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,cmd)\033[0m" + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,comm)\033[0m" + else + openim::log::error "Unsupported OS type: $OSTYPE" + fi + done + openim::log::error "Processes have not been stopped properly." + else + openim::log::success "All openim-msgtransfer processes have been stopped properly." + fi + return 0 +} + + ###################################### Linux Systemd ###################################### SYSTEM_FILE_PATH="/etc/systemd/system/${SERVER_NAME}.service" @@ -138,7 +164,7 @@ function openim::msgtransfer::uninstall() { openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" - set -o errexit + openim::log::info "uninstall ${SERVER_NAME} successfully" } diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh index 95da16c8a..8684f185c 100755 --- a/scripts/install/openim-push.sh +++ b/scripts/install/openim-push.sh @@ -40,9 +40,9 @@ # # Note: Ensure that the appropriate permissions and environmental variables are set prior to script execution. # -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh @@ -61,22 +61,20 @@ function openim::push::start() { OPENIM_PUSH_PORTS_ARRAY=$(openim::util::list-to-string ${OPENIM_PUSH_PORT} ) PUSH_PROM_PORTS_ARRAY=$(openim::util::list-to-string ${PUSH_PROM_PORT} ) - - openim::util::stop_services_with_name ${SERVER_NAME} - + openim::log::status "push port list: ${OPENIM_PUSH_PORTS_ARRAY[@]}" openim::log::status "prometheus port list: ${PUSH_PROM_PORTS_ARRAY[@]}" if [ ${#OPENIM_PUSH_PORTS_ARRAY[@]} -ne ${#PUSH_PROM_PORTS_ARRAY[@]} ]; then - openim::log::error_exit "The length of the two port lists is different!" + openim::log::error "The length of the two port lists is different!" fi for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done + return 0 - openim::util::check_process_names ${SERVER_NAME} } ###################################### Linux Systemd ###################################### @@ -125,7 +123,7 @@ function openim::push::uninstall() { openim::common::sudo "rm -f ${OPENIM_INSTALL_DIR}/${SERVER_NAME}" openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${SERVER_NAME}.yaml" openim::common::sudo "rm -f /etc/systemd/system/${SERVER_NAME}.service" - set -o errexit + openim::log::info "uninstall ${SERVER_NAME} successfully" } diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh index f8feaaa57..023cb6594 100755 --- a/scripts/install/openim-rpc.sh +++ b/scripts/install/openim-rpc.sh @@ -38,9 +38,9 @@ # Note: Before executing this script, ensure that the necessary permissions are granted and relevant environmental variables are set. # -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh @@ -64,6 +64,16 @@ IFS=" " read -ra OPENIM_RPC_SERVICE_TARGETS <<< "$(openim::rpc::service_name)" readonly OPENIM_RPC_SERVICE_TARGETS readonly OPENIM_RPC_SERVICE_LISTARIES=("${OPENIM_RPC_SERVICE_TARGETS[@]##*/}") + +OPENIM_ALL_RPC_FULL_PATH=() +for target in openim::rpc::service_name; do + OPENIM_ALL_RPC_FULL_PATH+=("${OPENIM_OUTPUT_HOSTBIN}/${target}") +done +readonly OPENIM_ALL_RPC_FULL_PATH + + + + # Make sure the environment is only called via common to avoid too much nesting openim::rpc::service_port() { local targets=( @@ -121,14 +131,11 @@ function openim::rpc::start() { printf "+------------------------+-------+-----------------+\n" done + # start all rpc services for ((i = 0; i < ${#OPENIM_RPC_SERVICE_LISTARIES[*]}; i++)); do - # openim::util::stop_services_with_name ${OPENIM_RPC_SERVICE_LISTARIES - openim::util::stop_services_on_ports ${OPENIM_RPC_PORT_LISTARIES[$i]} - openim::util::stop_services_on_ports ${OPENIM_RPC_PROM_PORT_LISTARIES[$i]} openim::log::info "OpenIM ${OPENIM_RPC_SERVICE_LISTARIES[$i]} config path: ${OPENIM_RPC_CONFIG}" - # Get the service and Prometheus ports. OPENIM_RPC_SERVICE_PORTS=( $(openim::util::list-to-string ${OPENIM_RPC_PORT_LISTARIES[$i]}) ) read -a OPENIM_RPC_SERVICE_PORTS_ARRAY <<< ${OPENIM_RPC_SERVICE_PORTS} @@ -138,15 +145,17 @@ function openim::rpc::start() { for ((j = 0; j < ${#OPENIM_RPC_SERVICE_PORTS_ARRAY[@]}; j++)); do openim::log::info "Starting ${OPENIM_RPC_SERVICE_LISTARIES[$i]} service, port: ${OPENIM_RPC_SERVICE_PORTS[j]}, prometheus port: ${OPENIM_RPC_PROM_PORTS[j]}, binary root: ${OPENIM_OUTPUT_HOSTBIN}/${OPENIM_RPC_SERVICE_LISTARIES[$i]}" - openim::rpc::start_service "${OPENIM_RPC_SERVICE_LISTARIES[$i]}" "${OPENIM_RPC_SERVICE_PORTS[j]}" "${OPENIM_RPC_PROM_PORTS[j]}" + result=$(openim::rpc::start_service "${OPENIM_RPC_SERVICE_LISTARIES[$i]}" "${OPENIM_RPC_SERVICE_PORTS[j]}" "${OPENIM_RPC_PROM_PORTS[j]}") + if [[ $? -ne 0 ]]; then + openim::log::error "start ${SERVER_NAME} failed" + else + openim::log::info "$result" + fi done done - sleep 5 - - openim::util::check_ports ${OPENIM_RPC_PORT_TARGETS[@]} - # openim::util::check_ports ${OPENIM_RPC_PROM_PORT_TARGETS[@]} + return 0 } function openim::rpc::start_service() { @@ -161,6 +170,7 @@ function openim::rpc::start_service() { cmd="${cmd} --prometheus_port ${prometheus_port}" fi nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + return 0 } ###################################### Linux Systemd ###################################### @@ -220,7 +230,7 @@ function openim::rpc::uninstall() { openim::common::sudo "rm -f ${OPENIM_CONFIG_DIR}/${service}.yaml" openim::common::sudo "rm -f ${SYSTEM_FILE_PATHS[$service]}" done - set -o errexit + openim::log::info "uninstall openim-rpc successfully" } diff --git a/scripts/install/openim-tools.sh b/scripts/install/openim-tools.sh index ac60a5f45..72005ed8e 100755 --- a/scripts/install/openim-tools.sh +++ b/scripts/install/openim-tools.sh @@ -38,9 +38,9 @@ # Example: ./openim-tools.sh openim::tools::install # -set -o errexit -set +o nounset -set -o pipefail + + + OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh @@ -122,7 +122,6 @@ function openim::tools::pre-start() { for tool in "${OPENIM_TOOLS_PRE_START_NAME_LISTARIES[@]}"; do openim::log::info "Starting ${tool}..." openim::tools::start_service ${tool} ${OPNEIM_CONFIG} - sleep 0.2 done } @@ -131,7 +130,6 @@ function openim::tools::post-start() { for tool in "${OPENIM_TOOLS_POST_START_NAME_LISTARIES[@]}"; do openim::log::info "Starting ${tool}..." openim::tools::start_service ${tool} - sleep 0.2 done } diff --git a/scripts/lib/golang.sh b/scripts/lib/golang.sh index af04771d5..7b9d7e60c 100755 --- a/scripts/lib/golang.sh +++ b/scripts/lib/golang.sh @@ -84,6 +84,57 @@ openim::golang::server_targets() { echo "${targets[@]}" } +openim::golang::server_targets_no_transfer() { + local targets=( + openim-api + openim-crontask + openim-msggateway + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user + ) + echo "${targets[@]}" +} + +openim::golang::server_targets_no_cmdutils() { + local targets=( + openim-api + openim-crontask + openim-msggateway + openim-msgtransfer + openim-push + openim-rpc-auth + openim-rpc-conversation + openim-rpc-friend + openim-rpc-group + openim-rpc-msg + openim-rpc-third + openim-rpc-user + ) + echo "${targets[@]}" +} + + +IFS=" " read -ra OPENIM_SERVER_TARGETS_NO_CMDUTILS <<< "$(openim::golang::server_targets_no_cmdutils)" +readonly OPENIM_SERVER_TARGETS_NO_CMDUTILS +readonly OPENIM_SERVER_BINARIES_NO_CMDUTILS=("${OPENIM_SERVER_TARGETS_NO_CMDUTILS[@]##*/}") + + + + + +IFS=" " read -ra OPENIM_SERVER_TARGETS_NO_TRANSFER <<< "$(openim::golang::server_targets_no_transfer)" +readonly OPENIM_SERVER_TARGETS_NO_TRANSFER +readonly OPENIM_SERVER_BINARIES_NO_TRANSFER=("${OPENIM_SERVER_TARGETS_NO_TRANSFER[@]##*/}") + + + + IFS=" " read -ra OPENIM_SERVER_TARGETS <<< "$(openim::golang::server_targets)" readonly OPENIM_SERVER_TARGETS readonly OPENIM_SERVER_BINARIES=("${OPENIM_SERVER_TARGETS[@]##*/}") diff --git a/scripts/lib/init.sh b/scripts/lib/init.sh index be8e9f8aa..4cd6d9fb8 100755 --- a/scripts/lib/init.sh +++ b/scripts/lib/init.sh @@ -13,9 +13,9 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -o errexit -set +o nounset -set -o pipefail + + + # Short-circuit if init.sh has already been sourced [[ $(type -t openim::init::loaded) == function ]] && return 0 diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index 8f2bb33cf..7afb6bfce 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -75,12 +75,13 @@ openim::log::errexit() { openim::log::install_errexit() { # trap ERR to provide an error handler whenever a command exits nonzero this - # is a more verbose version of set -o errexit - trap 'openim::log::errexit' ERR + # is a more verbose version of + # trap 'openim::log::errexit' ERR # setting errtrace allows our ERR trap handler to be propagated to functions, # expansions and subshells - set -o errtrace + #set -o errtrace + return 0 } # Print out the stack trace @@ -205,22 +206,27 @@ openim::log::status() { fi timestamp=$(date +"[%Y-%m-%d %H:%M:%S %Z]") - echo_log "+++ ${timestamp} ${1}" + echo_log "${timestamp} ${1}" shift for message; do echo_log " ${message}" done } + openim::log::success() { local V="${V:-0}" if [[ ${OPENIM_VERBOSE} < ${V} ]]; then return fi - timestamp=$(date +"%m%d %H:%M:%S") - echo_log -e "${COLOR_GREEN}[success ${timestamp}] ${COLOR_SUFFIX}==> " "$@" + local timestamp=$(date +"%m%d %H:%M:%S") + local reset_color='\033[0m' + echo_log -e "${COLOR_GREEN}[success ${timestamp}]${COLOR_SUFFIX}==> ${COLOR_GREEN}$@${reset_color}" } + + + function openim::log::test_log() { echo_log "test log" openim::log::info "openim::log::info" diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 1bdb7f640..4322255bb 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -298,7 +298,7 @@ openim::util::check_ports() { # An array to collect information about processes that are running. local started=() - openim::log::info "Checking ports: $*" + echo "Checking ports: $*" # Iterate over each given port. for port in "$@"; do # Initialize variables @@ -344,7 +344,7 @@ openim::util::check_ports() { # Print information about ports whose processes are not running. if [[ ${#not_started[@]} -ne 0 ]]; then - openim::log::info "\n### Not started ports:" + echo "### Not started ports:" for port in "${not_started[@]}"; do openim::log::error "Port $port is not started." done @@ -352,20 +352,20 @@ openim::util::check_ports() { # Print information about ports whose processes are running. if [[ ${#started[@]} -ne 0 ]]; then - openim::log::info "\n### Started ports:" + echo "### Started ports:" for info in "${started[@]}"; do - openim::log::info "$info" + echo "$info" done fi # If any of the processes is not running, return a status of 1. if [[ ${#not_started[@]} -ne 0 ]]; then - openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" - openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else - openim::log::success "All specified processes are running." + #openim::log::success "All specified ports are running." return 0 fi } @@ -373,7 +373,7 @@ openim::util::check_ports() { # set +o errexit # Sample call for testing: # openim::util::check_ports 10002 1004 12345 13306 -# set -o errexit +# # The `openim::util::check_process_names` function analyzes the state of processes based on given names. # It accepts multiple process names as arguments and prints: @@ -402,7 +402,6 @@ openim::util::check_process_names() { local not_started=() local started=() - openim::log::info "Checking processes: $*" # Iterate over each given process name for process_name in "$@"; do # Use `pgrep` to find process IDs related to the given process name @@ -430,24 +429,24 @@ openim::util::check_process_names() { # Print information if [[ ${#not_started[@]} -ne 0 ]]; then - openim::log::info "Not started processes:" + echo "Not started processes:" for process_name in "${not_started[@]}"; do - openim::log::error "Process $process_name is not started." + echo "Process $process_name is not started." done fi if [[ ${#started[@]} -ne 0 ]]; then echo - openim::log::info "Started processes:" + echo "Started processes:" for info in "${started[@]}"; do - openim::log::info "$info" + echo "$info" done fi # Return status if [[ ${#not_started[@]} -ne 0 ]]; then - openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" - openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 else @@ -457,6 +456,66 @@ openim::util::check_process_names() { fi } +openim::util::check_process_names_for_stop() { + # Function to get the port of a process + get_port() { + local pid=$1 + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + # Linux + ss -ltnp 2>/dev/null | grep $pid | awk '{print $4}' | cut -d ':' -f2 + elif [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + lsof -nP -iTCP -sTCP:LISTEN -a -p $pid | awk 'NR>1 {print $9}' | sed 's/.*://' + else + echo "Unsupported OS" + return 1 + fi + } + + # Arrays to collect details of processes + local not_started=() + local started=() + + + # Iterate over each given process name + for process_name in "$@"; do + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o cmd=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done + + + if [[ ${#started[@]} -ne 0 ]]; then + echo + echo "The programs that have not exited are:" + for info in "${started[@]}"; do + echo "$info " + done + return 1 + fi + + return 0 + +} + # openim::util::check_process_names docker-pr # The `openim::util::stop_services_on_ports` function stops services running on specified ports. @@ -473,7 +532,7 @@ openim::util::stop_services_on_ports() { # An array to collect information about processes that were stopped. local stopped=() - openim::log::info "Stopping services on ports: $*" + echo "Stopping services on ports: $*" # Iterate over each given port. for port in "$@"; do # Use the `lsof` command to find process information related to the given port. @@ -497,7 +556,7 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes couldn't be stopped. if [[ ${#not_stopped[@]} -ne 0 ]]; then - openim::log::info "Ports that couldn't be stopped:" + echo "Ports that couldn't be stopped:" for port in "${not_stopped[@]}"; do openim::log::status "Failed to stop service on port $port." done @@ -506,7 +565,7 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes were successfully stopped. if [[ ${#stopped[@]} -ne 0 ]]; then for port in "${stopped[@]}"; do - openim::log::info "Successfully stopped service on port $port." + echo "Successfully stopped service on port $port." done fi @@ -539,7 +598,7 @@ openim::util::stop_services_with_name() { # An array to collect information about processes that were stopped. local stopped=() - openim::log::info "Stopping services with names: $*" + echo "Stopping services with names: $*" # Iterate over each given service name. for server_name in "$@"; do # Use the `pgrep` command to find process IDs related to the given service name. @@ -573,26 +632,8 @@ openim::util::stop_services_with_name() { not_stopped+=("$server_name") fi done + return 0 - # Print information about services whose processes couldn't be stopped. - if [[ ${#not_stopped[@]} -ne 0 ]]; then - openim::log::info "Services that couldn't be stopped:" - for name in "${not_stopped[@]}"; do - openim::log::status "Failed to stop the $name service." - done - fi - - # Print information about services whose processes were successfully stopped. - if [[ ${#stopped[@]} -ne 0 ]]; then - echo - openim::log::info "Stopped services:" - for name in "${stopped[@]}"; do - openim::log::info "Successfully stopped the $name service." - done - fi - - openim::log::success "All specified services were stopped." - echo "" } # sleep 333333& # sleep 444444& @@ -1531,7 +1572,7 @@ openim::util::check_ports() { # An array to collect information about processes that are running. local started=() - openim::log::info "Checking ports: $*" + echo "Checking ports: $*" # Iterate over each given port. for port in "$@"; do # Initialize variables @@ -1577,7 +1618,7 @@ openim::util::check_ports() { # Print information about ports whose processes are not running. if [[ ${#not_started[@]} -ne 0 ]]; then - openim::log::info "\n### Not started ports:" + printf "\n### Not started ports:" for port in "${not_started[@]}"; do openim::log::error "Port $port is not started." done @@ -1585,16 +1626,16 @@ openim::util::check_ports() { # Print information about ports whose processes are running. if [[ ${#started[@]} -ne 0 ]]; then - openim::log::info "\n### Started ports:" + printf "\n### Started ports:" for info in "${started[@]}"; do - openim::log::info "$info" + echo "$info" done fi # If any of the processes is not running, return a status of 1. if [[ ${#not_started[@]} -ne 0 ]]; then - openim::color::echo $COLOR_RED " OpenIM Stdout Log >> cat ${LOG_FILE}" - openim::color::echo $COLOR_RED " OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stdout Log >> cat ${LOG_FILE}" + openim::color::echo $COLOR_RED "OpenIM Stderr Log >> cat ${STDERR_LOG_FILE}" echo "" cat "$TMP_LOG_FILE" | awk '{print "\033[31m" $0 "\033[0m"}' return 1 @@ -1607,7 +1648,7 @@ openim::util::check_ports() { # set +o errexit # Sample call for testing: # openim::util::check_ports 10002 1004 12345 13306 -# set -o errexit +# # The `openim::util::check_process_names` function analyzes the state of processes based on given names. # It accepts multiple process names as arguments and prints: @@ -1636,7 +1677,7 @@ openim::util::check_process_names() { local not_started=() local started=() - openim::log::info "Checking processes: $*" + echo "Checking processes: $*" # Iterate over each given process name for process_name in "$@"; do # Use `pgrep` to find process IDs related to the given process name @@ -1664,7 +1705,7 @@ openim::util::check_process_names() { # Print information if [[ ${#not_started[@]} -ne 0 ]]; then - openim::log::info "Not started processes:" + echo "Not started processes:" for process_name in "${not_started[@]}"; do openim::log::error "Process $process_name is not started." done @@ -1672,9 +1713,9 @@ openim::util::check_process_names() { if [[ ${#started[@]} -ne 0 ]]; then echo - openim::log::info "Started processes:" + echo "Started processes:" for info in "${started[@]}"; do - openim::log::info "$info" + echo "$info" done fi @@ -1707,7 +1748,7 @@ openim::util::stop_services_on_ports() { # An array to collect information about processes that were stopped. local stopped=() - openim::log::info "Stopping services on ports: $*" + echo "Stopping services on ports: $*" # Iterate over each given port. for port in "$@"; do # Use the `lsof` command to find process information related to the given port. @@ -1731,7 +1772,7 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes couldn't be stopped. if [[ ${#not_stopped[@]} -ne 0 ]]; then - openim::log::info "Ports that couldn't be stopped:" + echo "Ports that couldn't be stopped:" for port in "${not_stopped[@]}"; do openim::log::status "Failed to stop service on port $port." done @@ -1740,7 +1781,7 @@ openim::util::stop_services_on_ports() { # Print information about ports whose processes were successfully stopped. if [[ ${#stopped[@]} -ne 0 ]]; then for port in "${stopped[@]}"; do - openim::log::info "Successfully stopped service on port $port." + echo "Successfully stopped service on port $port." done fi @@ -1773,7 +1814,7 @@ openim::util::stop_services_with_name() { # An array to collect information about processes that were stopped. local stopped=() - openim::log::info "Stopping services with names: $*" + echo "Stopping services with names: $*" # Iterate over each given service name. for server_name in "$@"; do # Use the `pgrep` command to find process IDs related to the given service name. @@ -1810,7 +1851,7 @@ openim::util::stop_services_with_name() { # Print information about services whose processes couldn't be stopped. if [[ ${#not_stopped[@]} -ne 0 ]]; then - openim::log::info "Services that couldn't be stopped:" + echo "Services that couldn't be stopped:" for name in "${not_stopped[@]}"; do openim::log::status "Failed to stop the $name service." done @@ -1819,9 +1860,9 @@ openim::util::stop_services_with_name() { # Print information about services whose processes were successfully stopped. if [[ ${#stopped[@]} -ne 0 ]]; then echo - openim::log::info "Stopped services:" + echo "Stopped services:" for name in "${stopped[@]}"; do - openim::log::info "Successfully stopped the $name service." + echo "Successfully stopped the $name service." done fi @@ -2822,6 +2863,46 @@ function openim::util::gen_os_arch() { fi } + + +function openim::util::check_process_names_for_stop() { + local all_stopped=true + for service in "${OPENIM_ALL_SERVICE_LIBRARIES[@]}"; do + + PIDS=$(pgrep -f "${service}") || PIDS="0" + if [ "$PIDS" = "0" ]; then + continue + fi + + + NUM_PROCESSES=$(echo "$PIDS" | wc -l | xargs) + if [ "$NUM_PROCESSES" -gt 0 ]; then + all_stopped=false + echo "Found $NUM_PROCESSES processes for ${service}" + for PID in $PIDS; do + if [[ "$OSTYPE" == "linux-gnu"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,cmd)\033[0m" + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo -e "\033[31m$(ps -p $PID -o pid,comm)\033[0m" + else + openim::log::error "Unsupported OS type: $OSTYPE" + fi + done + echo "Processes for ${service} have not been stopped properly. " "$NUM_PROCESSES" + fi + done + + if [ "$all_stopped" = true ]; then + openim::log::success "All processes have been stopped properly." + return 0 + fi + + return 1 +} + + + + if [[ "$*" =~ openim::util:: ]];then eval $* -fi \ No newline at end of file +fi diff --git a/scripts/list-feature-tests.sh b/scripts/list-feature-tests.sh index 438474bae..d6eaa4873 100755 --- a/scripts/list-feature-tests.sh +++ b/scripts/list-feature-tests.sh @@ -18,9 +18,9 @@ # # Usage: `scripts/list-feature-tests.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. grep "\[Feature:\w+\]" "${OPENIM_ROOT}"/test/e2e/**/*.go -Eoh | LC_ALL=C sort -u \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh index a34d5ee22..eb8b04359 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -47,9 +47,9 @@ # images and other build artifacts. # Build a OpenIM release. This script supports various flags for flexible execution control. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/common.sh" source "${OPENIM_ROOT}/scripts/lib/release.sh" diff --git a/scripts/run-in-gopath.sh b/scripts/run-in-gopath.sh index 9bb8ec882..6af986975 100755 --- a/scripts/run-in-gopath.sh +++ b/scripts/run-in-gopath.sh @@ -20,9 +20,9 @@ # the project. # Usage: `scripts/run-in-gopath.sh `. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/start-all.sh b/scripts/start-all.sh index ca03f0c3c..9a98bfca3 100755 --- a/scripts/start-all.sh +++ b/scripts/start-all.sh @@ -16,70 +16,100 @@ #FIXME This script is the startup script for multiple servers. #FIXME The full names of the shell scripts that need to be started are placed in the `need_to_start_server_shell` array. -set -o nounset -set -o pipefail -OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. -source "${OPENIM_ROOT}/scripts/install/common.sh" -openim::log::info "\n# Begin to start all openim service scripts" -set +o errexit -openim::golang::check_openim_binaries -if [[ $? -ne 0 ]]; then - openim::log::error "OpenIM binaries are not found. Please run 'make build' to build binaries." - "${OPENIM_ROOT}"/scripts/build-all-service.sh -fi -set -o errexit - -"${OPENIM_ROOT}"/scripts/init-config.sh --skip +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/install/common.sh" -echo "You need to start the following scripts in order: ${OPENIM_SERVER_SCRIPTARIES[@]}" -openim::log::install_errexit # Function to execute the scripts. -function execute_scripts() { +function execute_start_scripts() { for script_path in "${OPENIM_SERVER_SCRIPT_START_LIST[@]}"; do # Extract the script name without extension for argument generation. script_name_with_prefix=$(basename "$script_path" .sh) - + # Remove the "openim-" prefix. script_name=${script_name_with_prefix#openim-} - + # Construct the argument based on the script name. arg="openim::${script_name}::start" - + # Check if the script file exists and is executable. if [[ -x "$script_path" ]]; then - openim::log::status "Starting script: ${script_path##*/}" # Log the script name. - + openim::log::info "Starting script: ${script_path##*/}" # Log the script name. + # Execute the script with the constructed argument. - "$script_path" "$arg" - - # Check if the script executed successfully. - if [[ $? -eq 0 ]]; then - openim::log::info "${script_path##*/} executed successfully." - else - openim::log::errexit "Error executing ${script_path##*/}." + result=$("$script_path" "$arg") + if [[ $? -ne 0 ]]; then + openim::log::error "Start script: ${script_path##*/} failed" + openim::log::error "$result" + return 1 fi + else openim::log::errexit "Script ${script_path##*/} is missing or not executable." + return 1 fi done - sleep 0.5 } + + + + + +openim::golang::check_openim_binaries +if [[ $? -ne 0 ]]; then + openim::log::error "OpenIM binaries are not found. Please run 'make build' to build binaries." + "${OPENIM_ROOT}"/scripts/build-all-service.sh +fi + + +"${OPENIM_ROOT}"/scripts/init-config.sh --skip + +echo "You need to start the following scripts in order: ${OPENIM_SERVER_SCRIPTARIES[@]}" + + # TODO Prelaunch tools, simple for now, can abstract functions later TOOLS_START_SCRIPTS_PATH=${START_SCRIPTS_PATH}/openim-tools.sh -openim::log::info "\n## Pre Starting OpenIM services" +openim::log::status "\n## Pre Starting OpenIM services" ${TOOLS_START_SCRIPTS_PATH} openim::tools::pre-start -openim::log::info "\n## Starting OpenIM services" -execute_scripts -openim::log::info "\n## Post Starting OpenIM services" +result=$("${OPENIM_ROOT}"/scripts/stop-all.sh) +if [[ $? -ne 0 ]]; then + echo "+++ cat openim log file >>> ${LOG_FILE}" + openim::log::error "Some programs have not exited; the start process is aborted .\n $result" + exit 1 +fi + + + +openim::log::status "\n## Starting openim scripts: " +execute_start_scripts + +sleep 2 + +result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check) +if [[ $? -ne 0 ]]; then + echo "+++ cat openim log file >>> ${LOG_FILE}" + openim::log::error "The program may fail to start.\n $result" + exit 1 +fi + + +result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}) +if [[ $? -ne 0 ]]; then + echo "+++ cat openim log file >>> ${LOG_FILE}" + openim::log::error "The program may fail to start.\n $result" + exit 1 +fi + + +openim::log::info "\n## Post Starting openim services" ${TOOLS_START_SCRIPTS_PATH} openim::tools::post-start -openim::color::echo $COLOR_BLUE "✨ All OpenIM services have been successfully started!" \ No newline at end of file +openim::log::success "All openim services have been successfully started!" \ No newline at end of file diff --git a/scripts/stop-all.sh b/scripts/stop-all.sh index 2acb9cdc5..b2572f7d5 100755 --- a/scripts/stop-all.sh +++ b/scripts/stop-all.sh @@ -18,22 +18,43 @@ # Usage: `scripts/stop.sh`. # Encapsulated as: `make stop`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/install/common.sh" -openim::log::info "\n# Begin to stop all openim service" +openim::log::status "Begin to stop all openim service" -echo "++ Ready to stop port: ${OPENIM_SERVER_PORT_LISTARIES[@]}" +openim::log::status "Stop all processes in the path ${OPENIM_OUTPUT_HOSTBIN}" -openim::util::stop_services_on_ports ${OPENIM_SERVER_PORT_LISTARIES[@]} +openim::util::stop_services_with_name "${OPENIM_OUTPUT_HOSTBIN}" +# todo OPENIM_ALL_SERVICE_LIBRARIES -echo -e "\n++ Stop all processes in the path ${OPENIM_OUTPUT_HOSTBIN}" -openim::util::stop_services_with_name "${OPENIM_OUTPUT_HOSTBIN}" -openim::log::success "✨ All processes to be killed" \ No newline at end of file + +max_retries=15 +attempt=0 + +while [[ $attempt -lt $max_retries ]] +do + result=$(openim::util::check_process_names_for_stop) + + if [[ $? -ne 0 ]]; then + if [[ $attempt -ne 0 ]] ; then + echo "+++ cat openim log file >>> ${LOG_FILE} " $attempt + openim::log::error "stop process failed. continue waiting\n" "${result}" + fi + sleep 1 + ((attempt++)) + else + openim::log::success " All openim processes to be stopped" + exit 0 + fi +done + +openim::log::error "openim processes stopped failed" +exit 1 diff --git a/scripts/update-generated-docs.sh b/scripts/update-generated-docs.sh index c37c4a1f9..d48a4067b 100755 --- a/scripts/update-generated-docs.sh +++ b/scripts/update-generated-docs.sh @@ -18,9 +18,9 @@ # immediately before exporting docs. We do not want to check these documents in # by default. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/update-yamlfmt.sh b/scripts/update-yamlfmt.sh index 90ec8aa62..24ec60de9 100755 --- a/scripts/update-yamlfmt.sh +++ b/scripts/update-yamlfmt.sh @@ -14,9 +14,9 @@ # limitations under the License. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/verify-pkg-names.sh b/scripts/verify-pkg-names.sh index 1459992e9..7fce3d7ad 100755 --- a/scripts/verify-pkg-names.sh +++ b/scripts/verify-pkg-names.sh @@ -17,9 +17,9 @@ # This script verifies whether codes follow golang convention. # Usage: `scripts/verify-pkg-names.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/verify-shellcheck.sh b/scripts/verify-shellcheck.sh index 8a5ad7321..c7e713d09 100755 --- a/scripts/verify-shellcheck.sh +++ b/scripts/verify-shellcheck.sh @@ -17,9 +17,9 @@ # This script lints each shell script by `shellcheck`. # Usage: `scripts/verify-shellcheck.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/verify-spelling.sh b/scripts/verify-spelling.sh index 2c02dccf7..fa0852866 100755 --- a/scripts/verify-spelling.sh +++ b/scripts/verify-spelling.sh @@ -17,9 +17,9 @@ # working directory by client9/misspell package. # Usage: `scripts/verify-spelling.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. export OPENIM_ROOT diff --git a/scripts/verify-typecheck.sh b/scripts/verify-typecheck.sh index 62fca4049..c9b2aaf30 100755 --- a/scripts/verify-typecheck.sh +++ b/scripts/verify-typecheck.sh @@ -16,9 +16,9 @@ # This script does a fast type check of script srnetes code for all platforms. # Usage: `scripts/verify-typecheck.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/verify-yamlfmt.sh b/scripts/verify-yamlfmt.sh index 3d0a0180d..3acbf457c 100755 --- a/scripts/verify-yamlfmt.sh +++ b/scripts/verify-yamlfmt.sh @@ -19,9 +19,9 @@ # # Usage: `scripts/verify-yamlfmt.sh`. -set -o errexit -set -o nounset -set -o pipefail + + + OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" From 2becd46bdd9e544101dcb45e9fe6fec62e8fbabf Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Tue, 27 Feb 2024 22:12:01 +0800 Subject: [PATCH 53/74] Fix lint errors in modified code in the /pkg directory (#1962) * /pkg-make lint Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> * /pkg_make_lint Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> --------- Signed-off-by: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> --- go.mod | 1 + go.sum | 2 ++ pkg/common/db/s3/aws/aws.go | 2 +- pkg/common/db/unrelation/user.go | 2 +- pkg/common/kafka/producer.go | 8 +++++++- pkg/rpcclient/notification/friend.go | 10 ++++++++-- pkg/rpcclient/notification/group.go | 13 +++++++------ pkg/rpcclient/notification/user.go | 8 ++++---- 8 files changed, 31 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 30bfcf146..6194cd544 100644 --- a/go.mod +++ b/go.mod @@ -121,6 +121,7 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect + github.com/smallstep/cli v0.25.2 // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index 6277afac3..7e181fd5d 100644 --- a/go.sum +++ b/go.sum @@ -312,6 +312,8 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/cli v0.25.2 h1:vkwnKLAAXj9vA3SJHqa0ytz2orHMUJn/glv79UuAthQ= +github.com/smallstep/cli v0.25.2/go.mod h1:g0jvoTvGKFhw2PeFXwA2E99k9rTX4J3Vc983i+jbVKM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go index 42d5cd14c..d996485c7 100644 --- a/pkg/common/db/s3/aws/aws.go +++ b/pkg/common/db/s3/aws/aws.go @@ -259,7 +259,7 @@ func (a *Aws) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, erro return res, nil } -// AccessURL todo +// AccessURL todo. func (a *Aws) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { // todo return "", nil diff --git a/pkg/common/db/unrelation/user.go b/pkg/common/db/unrelation/user.go index f5595c4eb..4c882fc64 100644 --- a/pkg/common/db/unrelation/user.go +++ b/pkg/common/db/unrelation/user.go @@ -64,7 +64,7 @@ func (u *UserMongoDriver) AddSubscriptionList(ctx context.Context, userID string } // iterate over aggregated results for cursor.Next(ctx) { - err := cursor.Decode(&cnt) + err = cursor.Decode(&cnt) if err != nil { return errs.Wrap(err) } diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index c2e0f33dc..bdb839404 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -29,6 +29,8 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "google.golang.org/protobuf/proto" + "fmt" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) @@ -118,7 +120,11 @@ func configureProducerAck(p *Producer, ackConfig string) { // configureCompression configures the message compression type for the producer. func configureCompression(p *Producer, compressType string) { var compress sarama.CompressionCodec = sarama.CompressionNone - compress.UnmarshalText(bytes.ToLower([]byte(compressType))) + err := compress.UnmarshalText(bytes.ToLower([]byte(compressType))) + if err != nil { + fmt.Printf("Failed to configure compression: %v\n", err) + return + } p.config.Producer.Compression = compress } diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index b98a1d38e..a55bc0e5e 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -109,6 +109,7 @@ func (f *FriendNotificationSender) getUsersInfoMap( return result, nil } +//nolint:unused func (f *FriendNotificationSender) getFromToUserNickname( ctx context.Context, fromUserID, toUserID string, @@ -214,7 +215,9 @@ func (f *FriendNotificationSender) BlackDeletedNotification(ctx context.Context, FromUserID: req.OwnerUserID, ToUserID: req.BlackUserID, }} - f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips) + if err := f.Notification(ctx, req.OwnerUserID, req.BlackUserID, constant.BlackDeletedNotification, &blackDeletedTips); err != nil { + //err + } } func (f *FriendNotificationSender) FriendInfoUpdatedNotification( @@ -223,5 +226,8 @@ func (f *FriendNotificationSender) FriendInfoUpdatedNotification( needNotifiedUserID string, ) { tips := sdkws.UserInfoUpdatedTips{UserID: changedUserID} - f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, constant.FriendInfoUpdatedNotification, &tips) + if err := f.Notification(ctx, mcontext.GetOpUserID(ctx), needNotifiedUserID, + constant.FriendInfoUpdatedNotification, &tips); err != nil { + // err + } } diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 7f40326b7..d336e4b12 100755 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -192,6 +192,7 @@ func (g *GroupNotificationSender) getGroupOwnerAndAdminUserID(ctx context.Contex return utils.Slice(members, fn), nil } +//nolint:unused func (g *GroupNotificationSender) groupDB2PB(group *relation.GroupModel, ownerUserID string, memberCount uint32) *sdkws.GroupInfo { return &sdkws.GroupInfo{ GroupID: group.GroupID, @@ -231,7 +232,7 @@ func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberM } } -func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { +/* func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error) { users, err := g.getUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -241,7 +242,7 @@ func (g *GroupNotificationSender) getUsersInfoMap(ctx context.Context, userIDs [ result[user.GetUserID()] = user.(*sdkws.UserInfo) } return result, nil -} +} */ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) { defer log.ZDebug(ctx, "return") @@ -266,11 +267,11 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws AppMangerLevel: constant.AppAdmin, } } else { - member, err := g.db.TakeGroupMember(ctx, groupID, userID) - if err == nil { + member, err2 := g.db.TakeGroupMember(ctx, groupID, userID) + if err2 == nil { *opUser = g.groupMemberDB2PB(member, 0) - } else if !errs.ErrRecordNotFound.Is(err) { - return err + } else if !errs.ErrRecordNotFound.Is(err2) { + return err2 } } } diff --git a/pkg/rpcclient/notification/user.go b/pkg/rpcclient/notification/user.go index 4347faece..13e5b6abb 100644 --- a/pkg/rpcclient/notification/user.go +++ b/pkg/rpcclient/notification/user.go @@ -71,7 +71,7 @@ func NewUserNotificationSender( return f } -func (u *UserNotificationSender) getUsersInfoMap( +/* func (u *UserNotificationSender) getUsersInfoMap( ctx context.Context, userIDs []string, ) (map[string]*sdkws.UserInfo, error) { @@ -84,9 +84,9 @@ func (u *UserNotificationSender) getUsersInfoMap( result[user.GetUserID()] = user.(*sdkws.UserInfo) } return result, nil -} +} */ -func (u *UserNotificationSender) getFromToUserNickname( +/* func (u *UserNotificationSender) getFromToUserNickname( ctx context.Context, fromUserID, toUserID string, ) (string, string, error) { @@ -95,7 +95,7 @@ func (u *UserNotificationSender) getFromToUserNickname( return "", "", nil } return users[fromUserID].Nickname, users[toUserID].Nickname, nil -} +} */ func (u *UserNotificationSender) UserStatusChangeNotification( ctx context.Context, From 613bacb78912f391ee5697a769c8d37622a82645 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Wed, 28 Feb 2024 11:01:30 +0800 Subject: [PATCH 54/74] cicd: bump League Patch (#1963) --- go.mod | 1 - go.sum | 2 -- internal/msgtransfer/init.go | 1 + pkg/common/kafka/consumer_group.go | 1 + 4 files changed, 2 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6194cd544..30bfcf146 100644 --- a/go.mod +++ b/go.mod @@ -121,7 +121,6 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sergi/go-diff v1.0.0 // indirect - github.com/smallstep/cli v0.25.2 // indirect github.com/src-d/gcfg v1.4.0 // indirect github.com/stretchr/objx v0.5.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index 7e181fd5d..6277afac3 100644 --- a/go.sum +++ b/go.sum @@ -312,8 +312,6 @@ github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smallstep/cli v0.25.2 h1:vkwnKLAAXj9vA3SJHqa0ytz2orHMUJn/glv79UuAthQ= -github.com/smallstep/cli v0.25.2/go.mod h1:g0jvoTvGKFhw2PeFXwA2E99k9rTX4J3Vc983i+jbVKM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index df06ec0f9..91a547440 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/OpenIMSDK/tools/errs" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net/http" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index d63527620..ed961b93e 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,6 +17,7 @@ package kafka import ( "context" "errors" + "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" From bcc6a95633e544f5493e70e0b54227b65100bc54 Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Thu, 29 Feb 2024 11:52:41 +0800 Subject: [PATCH 55/74] Fix lint errors in modified code (#1966) * makelint /internal 0228 Signed-off-by: xuan <146319162+wxuanF@users.noreply.github.com> * Update server.go --------- Signed-off-by: xuan <146319162+wxuanF@users.noreply.github.com> --- internal/push/consumer_init.go | 5 +++-- internal/rpc/friend/black.go | 2 +- internal/rpc/friend/friend.go | 16 ++++++++-------- internal/rpc/group/group.go | 28 ++++++++++++++-------------- internal/rpc/msg/as_read.go | 2 +- internal/rpc/msg/revoke.go | 4 ++-- internal/rpc/msg/send.go | 8 ++++---- internal/rpc/msg/server.go | 24 ++++++++++++------------ internal/rpc/third/s3.go | 6 +++--- internal/rpc/third/third.go | 2 +- internal/rpc/user/user.go | 4 ++-- 11 files changed, 51 insertions(+), 50 deletions(-) diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index daaa37e8a..92ce4714e 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -19,8 +19,9 @@ import ( ) type Consumer struct { - pushCh ConsumerHandler - successCount uint64 + pushCh ConsumerHandler + // successCount is unused + // successCount uint64 } func NewConsumer(pusher *Pusher) (*Consumer, error) { diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index ed5791c38..6bf780d3a 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -28,7 +28,7 @@ import ( ) func (s *friendServer) GetPaginationBlacks(ctx context.Context, req *pbfriend.GetPaginationBlacksReq) (resp *pbfriend.GetPaginationBlacksResp, err error) { - if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { return nil, err } total, blacks, err := s.blackDatabase.FindOwnerBlacks(ctx, req.UserID, req.Pagination) diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 84702f548..a85f758c5 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -115,7 +115,7 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.ApplyToAddFriendResp{} - if err := authverify.CheckAccessV3(ctx, req.FromUserID); err != nil { + if accessErr := authverify.CheckAccessV3(ctx, req.FromUserID); accessErr != nil { return nil, err } if req.ToUserID == req.FromUserID { @@ -124,7 +124,7 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply if err = CallbackBeforeAddFriend(ctx, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } - if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { + if _, getUserInfoErr := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); getUserInfoErr != nil { return nil, err } in1, in2, err := s.friendDatabase.CheckIn(ctx, req.FromUserID, req.ToUserID) @@ -219,7 +219,7 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) (resp *pbfriend.DeleteFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.DeleteFriendResp{} - if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.OwnerUserID); accessErr != nil { return nil, err } _, err = s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, []string{req.FriendUserID}) @@ -244,7 +244,7 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri return nil, err } resp = &pbfriend.SetFriendRemarkResp{} - if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.OwnerUserID); accessErr != nil { return nil, err } _, err = s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, []string{req.FriendUserID}) @@ -295,7 +295,7 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context, // ok 获取接收到的好友申请(即别人主动申请的). func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { return nil, err } total, friendRequests, err := s.friendDatabase.PageFriendRequestToMe(ctx, req.UserID, req.Pagination) @@ -315,7 +315,7 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetPaginationFriendsApplyFromResp{} - if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { return nil, err } total, friendRequests, err := s.friendDatabase.PageFriendRequestFromMe(ctx, req.UserID, req.Pagination) @@ -343,7 +343,7 @@ func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.GetPaginationFriendsReq) (resp *pbfriend.GetPaginationFriendsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { return nil, err } total, friends, err := s.friendDatabase.PageOwnerFriends(ctx, req.UserID, req.Pagination) @@ -361,7 +361,7 @@ func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.G func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriendIDsReq) (resp *pbfriend.GetFriendIDsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { + if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { return nil, err } resp = &pbfriend.GetFriendIDsResp{} diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 95f82266f..8bce182b8 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -539,7 +539,7 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, members...); err != nil { + if populateErr := s.PopulateGroupMember(ctx, members...); populateErr != nil { return nil, err } memberMap := make(map[string]*relationtb.GroupMemberModel) @@ -765,12 +765,12 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed") } var inGroup bool - if _, err := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil { + if _, takeErr := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); takeErr == nil { inGroup = true // 已经在群里了 } else if !s.IsNotFound(err) { return nil, err } - if _, err := s.User.GetPublicUserInfo(ctx, req.FromUserID); err != nil { + if _, getInfoErr := s.User.GetPublicUserInfo(ctx, req.FromUserID); getInfoErr != nil { return nil, err } var member *relationtb.GroupMemberModel @@ -857,14 +857,14 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) JoinTime: time.Now(), MuteEndTime: time.UnixMilli(0), } - if err := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); err != nil { + if callbackErr := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); callbackErr != nil { return nil, err } - if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil { + if createErr := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); createErr != nil { return nil, err } - if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); err != nil { + if createErr := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); createErr != nil { return nil, err } s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID) @@ -905,7 +905,7 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq) if member.RoleLevel == constant.GroupOwner { return nil, errs.ErrNoPermission.Wrap("group owner can't quit") } - if err := s.PopulateGroupMember(ctx, member); err != nil { + if populateErr := s.PopulateGroupMember(ctx, member); populateErr != nil { return nil, err } err = s.db.DeleteGroupMember(ctx, req.GroupID, []string{req.UserID}) @@ -967,14 +967,14 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, owner); err != nil { + if populateErr := s.PopulateGroupMember(ctx, owner); populateErr != nil { return nil, err } update := UpdateGroupInfoMap(ctx, req.GroupInfoForSet) if len(update) == 0 { return resp, nil } - if err := s.db.UpdateGroup(ctx, group.GroupID, update); err != nil { + if updateErr := s.db.UpdateGroup(ctx, group.GroupID, update); updateErr != nil { return nil, err } group, err = s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID) @@ -1168,7 +1168,7 @@ func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgrou if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, owners...); err != nil { + if populateErr := s.PopulateGroupMember(ctx, owners...); populateErr != nil { return nil, err } ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string { @@ -1200,17 +1200,17 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou return nil, errs.ErrNoPermission.Wrap("not group owner") } } - if err := s.PopulateGroupMember(ctx, owner); err != nil { + if populateErr := s.PopulateGroupMember(ctx, owner); populateErr != nil { return nil, err } group, err := s.db.TakeGroup(ctx, req.GroupID) if err != nil { return nil, err } - if req.DeleteMember == false && group.Status == constant.GroupStatusDismissed { + if !req.DeleteMember && group.Status == constant.GroupStatusDismissed { return nil, errs.ErrDismissedAlready.Wrap("group status is dismissed") } - if err := s.db.DismissGroup(ctx, req.GroupID, req.DeleteMember); err != nil { + if dismissErr := s.db.DismissGroup(ctx, req.GroupID, req.DeleteMember); dismissErr != nil { return nil, err } if !req.DeleteMember { @@ -1566,7 +1566,7 @@ func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req * if err != nil { return nil, err } - if err := s.PopulateGroupMember(ctx, owners...); err != nil { + if populateErr := s.PopulateGroupMember(ctx, owners...); populateErr != nil { return nil, err } ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string { diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index cb292421e..c1816ce18 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -82,7 +82,7 @@ func (m *msgServer) SetConversationHasReadSeq( if req.HasReadSeq > maxSeq { return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq") } - if err := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); err != nil { + if setErr := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); setErr != nil { return nil, err } if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 0a24753b2..7808ce87c 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -68,7 +68,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. if !authverify.IsAppManagerUid(ctx) { switch msgs[0].SessionType { case constant.SingleChatType: - if err := authverify.CheckAccessV3(ctx, msgs[0].SendID); err != nil { + if accessErr := authverify.CheckAccessV3(ctx, msgs[0].SendID); accessErr != nil { return nil, err } role = user.AppMangerLevel @@ -133,7 +133,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } else { recvID = msgs[0].RecvID } - if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil { + if notificationErr := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); notificationErr != nil { return nil, err } if err = CallbackAfterRevokeMsg(ctx, req); err != nil { diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 630b74a4a..d3e9f0a02 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -66,7 +66,7 @@ func (m *msgServer) sendMsgSuperGroupChat( return nil, err } - if err := callbackMsgModify(ctx, req); err != nil { + if modifyErr := callbackMsgModify(ctx, req); modifyErr != nil { return nil, err } err = m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData) @@ -144,7 +144,7 @@ func (m *msgServer) sendMsgNotification( } func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) { - if err := m.messageVerification(ctx, req); err != nil { + if verificationErr := m.messageVerification(ctx, req); verificationErr != nil { return nil, err } isSend := true @@ -169,10 +169,10 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq return nil, err } - if err := callbackMsgModify(ctx, req); err != nil { + if modifyErr := callbackMsgModify(ctx, req); modifyErr != nil { return nil, err } - if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil { + if mqErr := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); mqErr != nil { prommetrics.SingleChatMsgProcessFailedCounter.Inc() return nil, err } diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index fe1baa453..615097a1a 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,7 +15,6 @@ package msg import ( - "context" "google.golang.org/grpc" @@ -51,16 +50,17 @@ func (m *msgServer) addInterceptorHandler(interceptorFunc ...MessageInterceptorF m.Handlers = append(m.Handlers, interceptorFunc...) } -func (m *msgServer) execInterceptorHandler(ctx context.Context, req *msg.SendMsgReq) error { - for _, handler := range m.Handlers { - msgData, err := handler(ctx, req) - if err != nil { - return err - } - req.MsgData = msgData - } - return nil -} +// func `(*msgServer).execInterceptorHandler` is unused +// func (m *msgServer) execInterceptorHandler(ctx context.Context, req *msg.SendMsgReq) error { +// for _, handler := range m.Handlers { +// msgData, err := handler(ctx, req) +// if err != nil { +// return err +// } +// req.MsgData = msgData +// } +// return nil +// } func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { rdb, err := cache.NewRedis() @@ -71,7 +71,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if err != nil { return err } - if err := mongo.CreateMsgIndex(); err != nil { + if indexErr := mongo.CreateMsgIndex(); indexErr != nil { return err } cacheModel := cache.NewMsgCacheModel(rdb) diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 3b501d4ad..55ee08da7 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -75,7 +75,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In Group: req.Cause, CreateTime: time.Now(), } - if err := t.s3dataBase.SetObject(ctx, obj); err != nil { + if setObjectErr := t.s3dataBase.SetObject(ctx, obj); setObjectErr != nil { return nil, err } return &third.InitiateMultipartUploadResp{ @@ -257,10 +257,10 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) } var mate FormDataMate - if err := json.Unmarshal(data, &mate); err != nil { + if unmarshalErr := json.Unmarshal(data, &mate); unmarshalErr != nil { return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) } - if err := checkUploadName(ctx, mate.Name); err != nil { + if uploadErr := checkUploadName(ctx, mate.Name); uploadErr != nil { return nil, err } info, err := t.s3dataBase.StatObject(ctx, mate.Key) diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 7a63d3526..06a8622f3 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -56,7 +56,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if apiURL == "" { return fmt.Errorf("api url is empty") } - if _, err := url.Parse(config.Config.Object.ApiURL); err != nil { + if _, parseErr := url.Parse(config.Config.Object.ApiURL); parseErr != nil { return err } if apiURL[len(apiURL)-1] != '/' { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 6f9e2949f..0a9885121 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -123,11 +123,11 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI if err != nil { return nil, err } - if err := CallbackBeforeUpdateUserInfo(ctx, req); err != nil { + if callbackErr := CallbackBeforeUpdateUserInfo(ctx, req); callbackErr != nil { return nil, err } data := convert.UserPb2DBMap(req.UserInfo) - if err := s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil { + if updateErr := s.UpdateByMap(ctx, req.UserInfo.UserID, data); updateErr != nil { return nil, err } _ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID) From 1e7a301d093cb969482e9f65e01a3e22a20bf7f8 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Sat, 2 Mar 2024 10:41:20 +0800 Subject: [PATCH 56/74] cicd: bump League Patch (#1969) --- internal/rpc/msg/server.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 615097a1a..f38f6f66f 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,7 +15,6 @@ package msg import ( - "google.golang.org/grpc" "github.com/OpenIMSDK/protocol/constant" From 18f14c9722aba3764d0c339bc10566033ee687cd Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Sat, 2 Mar 2024 11:48:38 +0800 Subject: [PATCH 57/74] Update go-code.md (#1980) --- docs/contrib/go-code.md | 355 ++++++++++++++++++++++++++++++++++------ 1 file changed, 301 insertions(+), 54 deletions(-) diff --git a/docs/contrib/go-code.md b/docs/contrib/go-code.md index 1de448da7..b122917ca 100644 --- a/docs/contrib/go-code.md +++ b/docs/contrib/go-code.md @@ -26,19 +26,19 @@ jwt "github.com/dgrijalva/jwt-go/v4" ```go import ( -// go standard package -"fmt" - -// third party package -"github.com/jinzhu/gorm" -"github.com/spf13/cobra" -"github.com/spf13/viper" - -// Anonymous packages are grouped separately, and anonymous package references are explained -// import mysql driver -_ "github.com/jinzhu/gorm/dialects/mysql" - -// inner package + // go standard package + "fmt" + + // third party package + "github.com/jinzhu/gorm" + "github.com/spf13/cobra" + "github.com/spf13/viper" + + // Anonymous packages are grouped separately, and anonymous package references are explained + // import mysql driver + _ "github.com/jinzhu/gorm/dialects/mysql" + + // inner package ) ``` @@ -48,33 +48,33 @@ When multiple variables need to be used in a function, the `var` declaration can ```go var ( -Width int -Height int + Width int + Height int ) ``` - When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization. ```go -// bad -sptr := new(T) -sptr.Name = "bar" - -// good -sptr := &T{Name: "bar"} + // bad + sptr := new(T) + sptr.Name = "bar" + + // good + sptr := &T{Name: "bar"} ``` - The struct declaration and initialization format takes multiple lines and is defined as follows. ```go -type User struct{ - Username string - Email string -} - -user := User{ -Username: "belm", -Email: "nosbelm@qq.com", + type User struct{ + Username string + Email string + } + + user := User{ + Username: "belm", + Email: "nosbelm@qq.com", } ``` @@ -217,20 +217,20 @@ if err != nil { // bad v, err := foo() if err != nil || v == nil { -// error handling -return err + // error handling + return err } //good v, err := foo() if err != nil { -// error handling -return err + // error handling + return err } if v == nil { -// error handling -return errors. New("invalid value v") + // error handling + return errors. New("invalid value v") } ``` @@ -239,13 +239,14 @@ return errors. New("invalid value v") ```go v, err := f() if err != nil { - // error handling - return // or continue. + // error handling + return // or continue. } ``` - Bug description suggestions - Error descriptions start with a lowercase letter and do not end with punctuation, for example: + ```go // bad errors.New("Redis connection failed") @@ -254,6 +255,7 @@ errors.New("redis connection failed.") // good errors.New("redis connection failed") ``` + - Tell users what they can do, not what they can't. - When declaring a requirement, use must instead of should. For example, `must be greater than 0, must match regex '[a-z]+'`. - When declaring that a format is incorrect, use must not. For example, `must not contain`. @@ -359,18 +361,18 @@ u := User{ For example: -``` +```go // Seeking to an offset before the start of the file is an error. // Seeking to any positive offset is legal, but the behavior of subsequent // I/O operations on the underlying object are implementation-dependent. type Seeker interface { -Seek(offset int64, whence int) (int64, error) + Seek(offset int64, whence int) (int64, error) } // ReadWriter is the interface that groups the basic Read and Write methods. type ReadWriter interface { -reader -Writer + reader + Writer } ``` @@ -386,7 +388,7 @@ Writer Some common nouns are listed below. -``` +```go // A GonicMapper that contains a list of common initialisms taken from golang/lint var LintGonicMapper = GonicMapper{ "API": true, @@ -523,6 +525,7 @@ package genericclioptions // ErrSigningMethod defines invalid signing method error. var ErrSigningMethod = errors. New("Invalid signing method") ``` + - When there is a large block of constant or variable definition, you can comment a general description in front, and then comment the definition of the constant in detail before or at the end of each line of constant, for example: ```go // Code must start with 1xxxxx. @@ -567,7 +570,7 @@ Each function or method that needs to be exported must have a comment, the forma // BeforeUpdate run before update database record. func (p *Policy) BeforeUpdate() (err error) { // normal code -return nil + return nil } ``` @@ -743,9 +746,9 @@ for i := 0; i < 10; i++ { ```go // bad for file := range files { -fd, err := os. Open(file) -if err != nil { -return err + fd, err := os. Open(file) + if err != nil { + return err } defer fd. Close() // normal code @@ -753,14 +756,14 @@ defer fd. Close() //good for file := range files { -func() { -fd, err := os. Open(file) -if err != nil { -return err -} -defer fd. Close() -// normal code -}() + func() { + fd, err := os. Open(file) + if err != nil { + return err + } + defer fd. Close() + // normal code + }() } ``` @@ -888,6 +891,7 @@ type LogHandler struct { } var_http.Handler = LogHandler{} ``` + - When the server processes a request, it should create a context, save the relevant information of the request (such as requestID), and pass it in the function call chain. ### 9.1 Performance @@ -900,3 +904,246 @@ var_http.Handler = LogHandler{} - If you want to directly modify the value of the map, the value can only be a pointer, otherwise the original value must be overwritten. - map needs to be locked during concurrency. - The conversion of interface{} cannot be checked during compilation, it can only be checked at runtime, be careful to cause panic. + +## 10 Golang CI Lint + +- Golang CI Lint is a fast Go linters runner. It runs linters in parallel, uses caching, and works well with all environments, including CI. + +**In local development, you can use the following command to install Golang CI Lint: ** + +```bash +make lint +``` + +**In CI/CD, Check the Github Actions status code below after you submit the code directly** + +[![OpenIM golangci-lint](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/openimsdk/open-im-server/actions/workflows/golangci-lint.yml) + +golangci lint can select the types of tools, refer to the official documentation: [https://golangci-lint.run/usage/linters/](https://golangci-lint.run/usage/linters/) + +The types of comments we currently use include: [https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml](https://github.com/openimsdk/open-im-server/blob/main/.golangci.yml) the `linters.enable` field in the file. + +e.g: +```yaml +linters: + # please, do not use `enable-all`: it's deprecated and will be removed soon. + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + # enable-all: true + disable-all: true + enable: + - typecheck # Basic type checking + - gofmt # Format check + - govet # Go's standard linting tool + - gosimple # Suggestions for simplifying code + - misspell # Spelling mistakes + - staticcheck # Static analysis + - unused # Checks for unused code + - goimports # Checks if imports are correctly sorted and formatted + - godot # Checks for comment punctuation + - bodyclose # Ensures HTTP response body is closed + - errcheck # Checks for missed error returns + fast: true +``` + +Add that Chinese comments are not allowed in go code, please write a complete golangci lint specification on the basis of the above. + + +### 10.1 Configuration Document + +This configuration document is designed to configure the operational parameters of OpenIM (a hypothetical or specific code analysis tool), customize output formats, and provide detailed settings for specific code checkers (linters). Below is a summary of the document drafted based on the provided configuration information. + +#### 10.1 Runtime Options + +- **Concurrency** (`concurrency`): Default to use the available CPU count, can be manually set to 4 for parallel analysis. +- **Timeout** (`timeout`): Timeout duration for analysis operations, default is 1 minute, set here to 5 minutes. +- **Issue Exit Code** (`issues-exit-code`): Exit code defaults to 1 if at least one issue is found. +- **Test Files** (`tests`): Whether to include test files, defaults to true. +- **Build Tags** (`build-tags`): Specify build tags used by all linters, defaults to an empty list. Example adds `mytag`. +- **Skip Directories** (`skip-dirs`): Configure which directories' issues are not reported, defaults to empty, but some default directories are independently skipped. +- **Skip Files** (`skip-files`): Specify files where issues should not be reported, supports regular expressions. + +#### 10.2 Output Configuration + +- **Format** (`format`): Set output format, default is "colored-line-number". +- **Print Issued Lines** (`print-issued-lines`): Whether to print the lines where issues occur, defaults to true. +- **Print Linter Name** (`print-linter-name`): Whether to print the linter name at the end of issue text, defaults to true. +- **Uniqueness Filter** (`uniq-by-line`): Whether to make issue outputs unique per line, defaults to true. +- **Path Prefix** (`path-prefix`): Prefix to add to output file references, defaults to no prefix. +- **Sort Results** (`sort-results`): Sort results by file path, line number, and column number. + +#### 10.3 Linters Settings + +In the configuration file, the `linters-settings` section allows detailed configuration of individual linters. Below are examples of specific linters settings and their purposes: + +- **bidichk**: Used to check bidirectional text characters, ensuring correct display direction of text, especially when dealing with mixed left-to-right (LTR) and right-to-left (RTL) text. + +- **dogsled**: Monitors excessive use of blank identifiers (`_`) in assignment operations, which may obscure data processing errors or unclear logic. + +- **dupl**: Identifies duplicate code blocks, helping developers avoid code redundancy. The `threshold` parameter in settings allows adjustment of code similarity threshold triggering warnings. + +- **errcheck**: Checks for unhandled errors. In Go, error handling is achieved by checking function return values. This linter helps ensure all errors are properly handled. + +- **exhaustive**: Checks if `switch` statements include all possible values of an enum type, ensuring exhaustiveness of code. This helps avoid forgetting to handle certain cases. + +#### 10.4 Example: `errcheck` + +**Incorrect Code Example**: +```go +package main + +import ( + "fmt" + "os" +) + +func main() { + f, _ := os.Open("filename.ext") + defer f.Close() +} +``` + +**Issue**: In the above code, the error return value of `os.Open` function is explicitly ignored. This is a common mistake as it may lead to unhandled errors and hard-to-trace bugs. + +**Correct Form**: +```go +package main + +import ( + "fmt" + "os" +) + +func main() { + f, err := os.Open("filename.ext") + if err != nil { + fmt.Printf("error opening file: %v\n", err) + return + } + defer f.Close() +} +``` + +In the correct form, by checking the error (`err`) returned by `os.Open`, we gracefully handle error cases rather than simply ignoring them. + +#### 10.5 Example: `gofmt` + +**Incorrect Code Example**: +```go +package main +import "fmt" +func main() { +fmt.Println("Hello, world!") +} +``` + +**Issue**: This code snippet doesn't follow Go's standard formatting rules, for example, incorrect indentation of `fmt.Println`. + +**Correct Form**: +```go +package main + +import "fmt" + +func main() { + fmt.Println("Hello, world!") +} +``` + +Using `gofmt` tool can automatically fix such formatting issues, ensuring the code adheres to the coding standards of the Go community. + +#### 10.6 Example: `unused` + +**Incorrect Code Example**: +```go +package main + +func helper() {} + +func main() {} +``` + +**Issue**: The `helper` function is defined but not called anywhere, indicating potential redundant code or missing functionality implementation. + +**Correct Form**: +```go +package main + +// If the helper function is indeed needed, ensure it's used properly. +func helper() { + // Implement the function's functionality or ensure it's called elsewhere +} + +func main() { + helper() +} +``` + +To improve the section on Linters settings in the document, we'll expand with more detailed explanations and reinforce understanding through examples. + +#### 10.7 Example: `dogsled` + +**Incorrect Code Example**: +```go +func getValues() (int, int, int) { + return 1, 2, 3 +} + +func main() { + _, _, val := getValues() + fmt.Println(val) // Only interested in the third return value +} +``` + +**Explanation**: In the above code, we use two blank identifiers to ignore the first two return values. Excessive use of blank identifiers can make code reading difficult. + +**Improved Code**: +Consider refactoring the function or the usage of return values to reduce the need for blank identifiers or explicitly comment why ignoring certain values is safe. + +#### 10.8: `exhaustive` + +**Incorrect Code Example**: +```go +type Fruit int + +const ( + Apple Fruit = iota + Banana + Orange +) + +func getFruitName(f Fruit) string { + switch f { + case Apple: + return "Apple" + case Banana: + return "Banana" + // Missing handling for Orange + } + return "Unknown" +} +``` + +**Explanation**: In this code, the `switch` statement doesn't cover all possible values of the `Fruit` type; the case for `Orange` is missing. + +**Improved Code**: +```go +func getFruitName(f Fruit) string { + switch f { + case Apple: + return "Apple" + case Banana: + return "Banana" + case Orange: + return "Orange" + } + return "Unknown" +} +``` + +By adding the missing `case`, we ensure the `switch` statement is exhaustive, handling every possible enum value. + +#### 10.9 Optimization of Configuration Files and Application of Code Analysis Tools + +Through these examples, we demonstrate how to improve code quality by identifying and fixing common coding issues. OpenIM's configuration files allow developers to customize linters' behavior according to project requirements, ensuring code compliance with predefined quality standards and style guidelines. + +By employing these tools and configuration strategies, teams can reduce the number of bugs, enhance code maintainability, and facilitate efficient collaboration during code review processes. From b511dc7ec461d855f72b75298f928601189d6b90 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Sat, 2 Mar 2024 14:31:01 +0800 Subject: [PATCH 58/74] Update test.sh (#1968) --- scripts/install/test.sh | 43 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/scripts/install/test.sh b/scripts/install/test.sh index 4a78e4504..4ea3f16ca 100755 --- a/scripts/install/test.sh +++ b/scripts/install/test.sh @@ -34,8 +34,8 @@ # # The root of the build/dist directory -IAM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. -[[ -z ${COMMON_SOURCED} ]] && source ${IAM_ROOT}/scripts/install/common.sh +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/../.. +[[ -z ${COMMON_SOURCED} ]] && source ${OPENIM_ROOT}/scripts/install/common.sh # API Server API Address:Port INSECURE_OPENIMAPI="http://${OPENIM_API_HOST}:${API_OPENIM_PORT}" @@ -72,7 +72,7 @@ function openim::test::auth() { # Define a function to get a token for a specific user openim::test::get_token() { - local user_id="${1:-openIM123456}" # Default user ID if not provided + local user_id="${1:-imAdmin}" # Default user ID if not provided token_response=$( ${CCURL} "${OperationID}" "${Header}" ${INSECURE_OPENIMAPI}/auth/user_token \ -d'{"secret": "'"$SECRET"'","platformID": 1,"userID": "'$user_id'"}' @@ -142,10 +142,7 @@ openim::test::check_user_account() { cat < Date: Sun, 3 Mar 2024 21:15:27 +0800 Subject: [PATCH 59/74] docs: update openim server readme github codespaces (#1984) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e83afe5aa..1552f62e5 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,8 @@ We support many platforms. Here are the addresses for quick experience on the we ## :hammer_and_wrench: To Start Developing OpenIM +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/openimsdk/open-im-server) + [![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) OpenIM Our goal is to build a top-level open source community. We have a set of standards, in the [Community repository](https://github.com/OpenIMSDK/community). From 57331182c2d034c42383c282b31e4e5d61165325 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Mon, 4 Mar 2024 10:45:04 +0800 Subject: [PATCH 60/74] feat: update golangci.yml file add and delete some linters (#1989) --- .golangci.yml | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index b129ffd68..0a0d40c21 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -40,10 +40,14 @@ run: # "/" will be replaced by current OS file path separator to properly work # on Windows. skip-dirs: + - components + - docs - util - .*~ - api/swagger/docs - server/docs + - components/mnt/config/certs + - logs # default is true. Enables skipping of directories: # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ @@ -58,6 +62,11 @@ run: skip-files: - ".*\\.my\\.go$" - _test.go + - ".*_test.go" + - "mocks/" + - ".github/" + - "logs/" + - "components/" # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": # If invoked with -mod=readonly, the go command is disallowed from the implicit @@ -131,8 +140,8 @@ linters-settings: # path to a file containing a list of functions to exclude from checking # see https://github.com/kisielk/errcheck#excluding-functions for details - #exclude: errcheck.txt - + # exclude: errcheck.txt + errorlint: # Check whether fmt.Errorf uses the %w verb for formatting errors. See the readme for caveats errorf: true @@ -418,7 +427,7 @@ linters-settings: govet: # report about shadowed variables - check-shadowing: true + check-shadowing: false # settings per analyzer settings: @@ -489,9 +498,9 @@ linters-settings: - github.com\/user\/package\/v4\.Type lll: - # max line length, lines longer will be reported. Default is 120. + # max line length, lines longer will be reported. Default is 250. # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 240 + line-length: 250 # tab width in spaces. Default to 1. tab-width: 4 maligned: @@ -715,17 +724,19 @@ linters: # enable-all: true disable-all: true enable: - - typecheck # 基本的类型检查 - - gofmt # 格式检查 - - govet # Go 语言的标准检查工具 - - gosimple # 简化代码的建议 - - misspell # 拼写错误 - - staticcheck # 静态检查 - - unused # 未使用的代码检查 - - goimports # 检查导入是否正确排序和格式化 - - godot # 注释句点检查 - - bodyclose # 确保 HTTP response body 被关闭 - - errcheck # 检查是否遗漏了错误返回值 + - typecheck # Basic type checking + - gofmt # Format check + - govet # Go's standard linting tool + - gosimple # Suggestions for simplifying code + - misspell # Spelling mistakes + - staticcheck # Static analysis + - unused # Checks for unused code + - goimports # Checks if imports are correctly sorted and formatted + - godot # Checks for comment punctuation + - bodyclose # Ensures HTTP response body is closed + - stylecheck # Style checker for Go code + - unused # Checks for unused code + - errcheck # Checks for missed error returns fast: true issues: @@ -792,6 +803,11 @@ issues: - lll source: "^//go:generate " + - text: ".*[\u4e00-\u9fa5]+.*" + linters: + - golint + source: "^//.*$" + # Independently from option `exclude` we use default exclude patterns, # it can be disabled by this option. To list all # excluded by default patterns execute `golangci-lint run --help`. @@ -852,4 +868,4 @@ severity: rules: - linters: - dupl - severity: info \ No newline at end of file + severity: info From 1ef26b29a744a8b45c0c8c48352004c7f9051c47 Mon Sep 17 00:00:00 2001 From: xuan <146319162+wxuanF@users.noreply.github.com> Date: Mon, 4 Mar 2024 11:18:38 +0800 Subject: [PATCH 61/74] back err_name (#1976) Signed-off-by: xuan <146319162+wxuanF@users.noreply.github.com> --- internal/msgtransfer/init.go | 2 +- internal/rpc/friend/black.go | 2 +- internal/rpc/friend/friend.go | 16 ++++++++-------- internal/rpc/group/group.go | 24 ++++++++++++------------ internal/rpc/msg/as_read.go | 2 +- internal/rpc/msg/revoke.go | 4 ++-- internal/rpc/msg/send.go | 8 ++++---- internal/rpc/msg/server.go | 2 +- internal/rpc/third/s3.go | 6 +++--- internal/rpc/third/third.go | 2 +- internal/rpc/user/user.go | 4 ++-- 11 files changed, 36 insertions(+), 36 deletions(-) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 91a547440..9b1b853f2 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -70,7 +70,7 @@ func StartTransfer(prometheusPort int) error { return err } - if err2 := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err2 != nil { + if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { return err } client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 6bf780d3a..ed5791c38 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -28,7 +28,7 @@ import ( ) func (s *friendServer) GetPaginationBlacks(ctx context.Context, req *pbfriend.GetPaginationBlacksReq) (resp *pbfriend.GetPaginationBlacksResp, err error) { - if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } total, blacks, err := s.blackDatabase.FindOwnerBlacks(ctx, req.UserID, req.Pagination) diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index a85f758c5..84702f548 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -115,7 +115,7 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.ApplyToAddFriendResp{} - if accessErr := authverify.CheckAccessV3(ctx, req.FromUserID); accessErr != nil { + if err := authverify.CheckAccessV3(ctx, req.FromUserID); err != nil { return nil, err } if req.ToUserID == req.FromUserID { @@ -124,7 +124,7 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply if err = CallbackBeforeAddFriend(ctx, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } - if _, getUserInfoErr := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); getUserInfoErr != nil { + if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { return nil, err } in1, in2, err := s.friendDatabase.CheckIn(ctx, req.FromUserID, req.ToUserID) @@ -219,7 +219,7 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) (resp *pbfriend.DeleteFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.DeleteFriendResp{} - if accessErr := s.userRpcClient.Access(ctx, req.OwnerUserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { return nil, err } _, err = s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, []string{req.FriendUserID}) @@ -244,7 +244,7 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri return nil, err } resp = &pbfriend.SetFriendRemarkResp{} - if accessErr := s.userRpcClient.Access(ctx, req.OwnerUserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.OwnerUserID); err != nil { return nil, err } _, err = s.friendDatabase.FindFriendsWithError(ctx, req.OwnerUserID, []string{req.FriendUserID}) @@ -295,7 +295,7 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context, // ok 获取接收到的好友申请(即别人主动申请的). func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } total, friendRequests, err := s.friendDatabase.PageFriendRequestToMe(ctx, req.UserID, req.Pagination) @@ -315,7 +315,7 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetPaginationFriendsApplyFromResp{} - if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } total, friendRequests, err := s.friendDatabase.PageFriendRequestFromMe(ctx, req.UserID, req.Pagination) @@ -343,7 +343,7 @@ func (s *friendServer) IsFriend(ctx context.Context, req *pbfriend.IsFriendReq) func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.GetPaginationFriendsReq) (resp *pbfriend.GetPaginationFriendsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } total, friends, err := s.friendDatabase.PageOwnerFriends(ctx, req.UserID, req.Pagination) @@ -361,7 +361,7 @@ func (s *friendServer) GetPaginationFriends(ctx context.Context, req *pbfriend.G func (s *friendServer) GetFriendIDs(ctx context.Context, req *pbfriend.GetFriendIDsReq) (resp *pbfriend.GetFriendIDsResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if accessErr := s.userRpcClient.Access(ctx, req.UserID); accessErr != nil { + if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { return nil, err } resp = &pbfriend.GetFriendIDsResp{} diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 8bce182b8..9b1198d58 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -539,7 +539,7 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou if err != nil { return nil, err } - if populateErr := s.PopulateGroupMember(ctx, members...); populateErr != nil { + if err := s.PopulateGroupMember(ctx, members...); err != nil { return nil, err } memberMap := make(map[string]*relationtb.GroupMemberModel) @@ -765,12 +765,12 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed") } var inGroup bool - if _, takeErr := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); takeErr == nil { + if _, err := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil { inGroup = true // 已经在群里了 } else if !s.IsNotFound(err) { return nil, err } - if _, getInfoErr := s.User.GetPublicUserInfo(ctx, req.FromUserID); getInfoErr != nil { + if _, err := s.User.GetPublicUserInfo(ctx, req.FromUserID); err != nil { return nil, err } var member *relationtb.GroupMemberModel @@ -857,14 +857,14 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) JoinTime: time.Now(), MuteEndTime: time.UnixMilli(0), } - if callbackErr := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); callbackErr != nil { + if err := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); err != nil { return nil, err } - if createErr := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); createErr != nil { + if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil { return nil, err } - if createErr := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); createErr != nil { + if err := s.conversationRpcClient.GroupChatFirstCreateConversation(ctx, req.GroupID, []string{req.InviterUserID}); err != nil { return nil, err } s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID) @@ -905,7 +905,7 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq) if member.RoleLevel == constant.GroupOwner { return nil, errs.ErrNoPermission.Wrap("group owner can't quit") } - if populateErr := s.PopulateGroupMember(ctx, member); populateErr != nil { + if err := s.PopulateGroupMember(ctx, member); err != nil { return nil, err } err = s.db.DeleteGroupMember(ctx, req.GroupID, []string{req.UserID}) @@ -967,7 +967,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf if err != nil { return nil, err } - if populateErr := s.PopulateGroupMember(ctx, owner); populateErr != nil { + if err := s.PopulateGroupMember(ctx, owner); err != nil { return nil, err } update := UpdateGroupInfoMap(ctx, req.GroupInfoForSet) @@ -1168,7 +1168,7 @@ func (s *groupServer) GetUserReqApplicationList(ctx context.Context, req *pbgrou if err != nil { return nil, err } - if populateErr := s.PopulateGroupMember(ctx, owners...); populateErr != nil { + if err := s.PopulateGroupMember(ctx, owners...); err != nil { return nil, err } ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string { @@ -1200,7 +1200,7 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou return nil, errs.ErrNoPermission.Wrap("not group owner") } } - if populateErr := s.PopulateGroupMember(ctx, owner); populateErr != nil { + if err := s.PopulateGroupMember(ctx, owner); err != nil { return nil, err } group, err := s.db.TakeGroup(ctx, req.GroupID) @@ -1210,7 +1210,7 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou if !req.DeleteMember && group.Status == constant.GroupStatusDismissed { return nil, errs.ErrDismissedAlready.Wrap("group status is dismissed") } - if dismissErr := s.db.DismissGroup(ctx, req.GroupID, req.DeleteMember); dismissErr != nil { + if err := s.db.DismissGroup(ctx, req.GroupID, req.DeleteMember); err != nil { return nil, err } if !req.DeleteMember { @@ -1566,7 +1566,7 @@ func (s *groupServer) GetGroupUsersReqApplicationList(ctx context.Context, req * if err != nil { return nil, err } - if populateErr := s.PopulateGroupMember(ctx, owners...); populateErr != nil { + if err := s.PopulateGroupMember(ctx, owners...); err != nil { return nil, err } ownerMap := utils.SliceToMap(owners, func(e *relationtb.GroupMemberModel) string { diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index c1816ce18..cb292421e 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -82,7 +82,7 @@ func (m *msgServer) SetConversationHasReadSeq( if req.HasReadSeq > maxSeq { return nil, errs.ErrArgs.Wrap("hasReadSeq must not be bigger than maxSeq") } - if setErr := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); setErr != nil { + if err := m.MsgDatabase.SetHasReadSeq(ctx, req.UserID, req.ConversationID, req.HasReadSeq); err != nil { return nil, err } if err = m.sendMarkAsReadNotification(ctx, req.ConversationID, constant.SingleChatType, req.UserID, diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 7808ce87c..0a24753b2 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -68,7 +68,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. if !authverify.IsAppManagerUid(ctx) { switch msgs[0].SessionType { case constant.SingleChatType: - if accessErr := authverify.CheckAccessV3(ctx, msgs[0].SendID); accessErr != nil { + if err := authverify.CheckAccessV3(ctx, msgs[0].SendID); err != nil { return nil, err } role = user.AppMangerLevel @@ -133,7 +133,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } else { recvID = msgs[0].RecvID } - if notificationErr := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); notificationErr != nil { + if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil { return nil, err } if err = CallbackAfterRevokeMsg(ctx, req); err != nil { diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index d3e9f0a02..630b74a4a 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -66,7 +66,7 @@ func (m *msgServer) sendMsgSuperGroupChat( return nil, err } - if modifyErr := callbackMsgModify(ctx, req); modifyErr != nil { + if err := callbackMsgModify(ctx, req); err != nil { return nil, err } err = m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData) @@ -144,7 +144,7 @@ func (m *msgServer) sendMsgNotification( } func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, err error) { - if verificationErr := m.messageVerification(ctx, req); verificationErr != nil { + if err := m.messageVerification(ctx, req); err != nil { return nil, err } isSend := true @@ -169,10 +169,10 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq return nil, err } - if modifyErr := callbackMsgModify(ctx, req); modifyErr != nil { + if err := callbackMsgModify(ctx, req); err != nil { return nil, err } - if mqErr := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); mqErr != nil { + if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil { prommetrics.SingleChatMsgProcessFailedCounter.Inc() return nil, err } diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index f38f6f66f..c25879276 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -70,7 +70,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if err != nil { return err } - if indexErr := mongo.CreateMsgIndex(); indexErr != nil { + if err := mongo.CreateMsgIndex(); err != nil { return err } cacheModel := cache.NewMsgCacheModel(rdb) diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 55ee08da7..3b501d4ad 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -75,7 +75,7 @@ func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.In Group: req.Cause, CreateTime: time.Now(), } - if setObjectErr := t.s3dataBase.SetObject(ctx, obj); setObjectErr != nil { + if err := t.s3dataBase.SetObject(ctx, obj); err != nil { return nil, err } return &third.InitiateMultipartUploadResp{ @@ -257,10 +257,10 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) } var mate FormDataMate - if unmarshalErr := json.Unmarshal(data, &mate); unmarshalErr != nil { + if err := json.Unmarshal(data, &mate); err != nil { return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) } - if uploadErr := checkUploadName(ctx, mate.Name); uploadErr != nil { + if err := checkUploadName(ctx, mate.Name); err != nil { return nil, err } info, err := t.s3dataBase.StatObject(ctx, mate.Key) diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 06a8622f3..7a63d3526 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -56,7 +56,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if apiURL == "" { return fmt.Errorf("api url is empty") } - if _, parseErr := url.Parse(config.Config.Object.ApiURL); parseErr != nil { + if _, err := url.Parse(config.Config.Object.ApiURL); err != nil { return err } if apiURL[len(apiURL)-1] != '/' { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 0a9885121..6f9e2949f 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -123,11 +123,11 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI if err != nil { return nil, err } - if callbackErr := CallbackBeforeUpdateUserInfo(ctx, req); callbackErr != nil { + if err := CallbackBeforeUpdateUserInfo(ctx, req); err != nil { return nil, err } data := convert.UserPb2DBMap(req.UserInfo) - if updateErr := s.UpdateByMap(ctx, req.UserInfo.UserID, data); updateErr != nil { + if err := s.UpdateByMap(ctx, req.UserInfo.UserID, data); err != nil { return nil, err } _ = s.friendNotificationSender.UserInfoUpdatedNotification(ctx, req.UserInfo.UserID) From 853ac47e4274fa04997665b3832e7e0c8ba67bde Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Mon, 4 Mar 2024 12:12:14 +0800 Subject: [PATCH 62/74] feat: Integrate Comprehensive E2E Testing for GoChat (#1906) * feat: create e2e test readme Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> * feat: fix markdown file * feat: add openim make lint * feat: add git chglog pull request * feat: add git chglog pull request * fix: fix openim api err code * fix: fix openim api err code * fix: fix openim api err code * feat: Improve CICD * feat: Combining GitHub and Google Workspace for Effective Project Management' * feat: fix openim tools error code * feat: fix openim tools error code * feat: add openim error handle * feat: add openim error handle * feat: optimize tim white prom code return err * feat: fix openim tools error code * style: format openim server code style * feat: add openim optimize commit code * feat: add openim optimize commit code * feat: add openim auto format code * feat: add openim auto format code * feat: add openim auto format code * feat: add openim auto format code * feat: add openim auto format code * feat: format openim code * feat: Some of the notes were translated * feat: Some of the notes were translated * feat: update openim server code * feat: optimize openim reset code * feat: optimize openim reset code --------- Signed-off-by: Xinwei Xiong (cubxxw) <3293172751nss@gmail.com> --- .github/workflows/e2e-test.yml | 2 + .github/workflows/pull-request.yml | 11 ++ CHANGELOG/CHANGELOG-3.5.md | 22 ++- Makefile | 2 +- cmd/openim-api/main.go | 10 +- cmd/openim-push/main.go | 2 +- cmd/openim-rpc/openim-rpc-auth/main.go | 2 +- .../openim-rpc-conversation/main.go | 2 +- cmd/openim-rpc/openim-rpc-friend/main.go | 2 +- cmd/openim-rpc/openim-rpc-group/main.go | 2 +- cmd/openim-rpc/openim-rpc-msg/main.go | 2 +- cmd/openim-rpc/openim-rpc-third/main.go | 2 +- cmd/openim-rpc/openim-rpc-user/main.go | 2 +- docs/contrib/code-conventions.md | 21 ++- internal/api/custom_validator.go | 11 +- internal/api/msg.go | 9 +- internal/api/route.go | 14 +- internal/api/user.go | 4 +- internal/msggateway/client.go | 37 ++--- internal/msggateway/compressor.go | 42 +++--- internal/msggateway/compressor_test.go | 13 ++ internal/msggateway/encoder.go | 4 +- internal/msggateway/init.go | 9 +- internal/msggateway/long_conn.go | 16 ++- internal/msggateway/message_handler.go | 32 +++-- internal/msggateway/n_ws_server.go | 7 +- internal/msggateway/options.go | 10 +- internal/msgtransfer/init.go | 13 +- .../msgtransfer/online_history_msg_handler.go | 14 +- internal/push/callback.go | 9 +- internal/push/offlinepush/fcm/push.go | 6 +- internal/push/push_to_client.go | 3 +- internal/rpc/conversation/conversaion.go | 6 +- internal/rpc/friend/friend.go | 4 +- internal/rpc/group/group.go | 4 +- internal/rpc/third/third.go | 2 +- internal/tools/conversation.go | 8 +- internal/tools/cron_task.go | 8 +- internal/tools/msg.go | 4 +- pkg/apistruct/msg.go | 1 + pkg/authverify/token.go | 3 +- pkg/common/cmd/api.go | 31 ++++- pkg/common/cmd/cron_task.go | 4 + pkg/common/cmd/msg_gateway.go | 55 ++++++-- pkg/common/cmd/msg_gateway_test.go | 2 +- pkg/common/cmd/msg_transfer.go | 18 ++- pkg/common/cmd/msg_utils.go | 3 +- pkg/common/cmd/root.go | 52 ++++--- pkg/common/cmd/rpc.go | 131 +++++++++--------- pkg/common/config/parse.go | 89 ++++++++---- pkg/common/config/parse_test.go | 4 +- pkg/common/convert/black.go | 6 +- pkg/common/convert/friend.go | 9 +- pkg/common/db/cache/black.go | 6 +- pkg/common/db/cache/init_redis.go | 16 ++- pkg/common/db/cache/meta_cache.go | 14 +- pkg/common/db/cache/msg.go | 7 +- pkg/common/db/cache/user.go | 12 +- pkg/common/db/controller/auth.go | 16 +-- pkg/common/db/controller/black.go | 24 ++-- pkg/common/db/controller/conversation.go | 44 +++--- pkg/common/db/controller/friend.go | 70 ++++++---- pkg/common/db/controller/group.go | 52 +++++-- pkg/common/db/controller/msg.go | 51 ++++--- pkg/common/db/controller/third.go | 8 +- pkg/common/db/s3/aws/aws.go | 4 +- pkg/common/db/s3/cont/consts.go | 26 +++- pkg/common/db/s3/cont/controller.go | 8 +- pkg/common/db/s3/cont/structs.go | 11 +- pkg/common/db/s3/minio/image.go | 90 +++++------- pkg/common/db/s3/oss/oss.go | 31 +++-- pkg/common/db/table/relation/group.go | 4 +- pkg/common/db/table/relation/user.go | 4 +- pkg/common/db/unrelation/msg.go | 124 +++++------------ .../discoveryregister/direct/directconn.go | 5 +- .../discoveryregister/discoveryregister.go | 3 +- .../discoveryregister_test.go | 2 +- pkg/common/http/http_client.go | 14 +- pkg/common/kafka/consumer.go | 36 ++--- pkg/common/kafka/producer.go | 5 +- pkg/common/kafka/util.go | 9 +- pkg/common/prommetrics/prommetrics.go | 1 - pkg/common/startrpc/start.go | 2 +- pkg/common/tls/tls.go | 36 +++-- pkg/msgprocessor/conversation.go | 3 +- pkg/rpcclient/auth.go | 3 +- pkg/rpcclient/conversation.go | 10 +- pkg/rpcclient/friend.go | 5 +- pkg/rpcclient/group.go | 3 +- pkg/rpcclient/msg.go | 22 ++- pkg/rpcclient/notification/conversation.go | 4 +- pkg/rpcclient/notification/friend.go | 2 +- pkg/rpcclient/push.go | 8 +- pkg/rpcclient/third.go | 39 ++++-- pkg/rpcclient/user.go | 3 +- pkg/util/genutil/genutil.go | 8 +- scripts/install/openim-tools.sh | 8 +- scripts/make-rules/golang.mk | 2 +- test/codescan/main.go | 1 + test/e2e/README.md | 2 + test/e2e/api/token/token.go | 15 -- test/e2e/framework/config/config_test.go | 2 +- test/e2e/framework/helpers/chat/chat.go | 37 ++--- test/e2e/page/chat_page.go | 1 + test/e2e/page/login_page.go | 1 + test/e2e/web/.keep | 1 - test/e2e/web/Readme.md | 2 + .../chat/cmd/conversion-chat/chat.go | 18 +-- tools/data-conversion/chat/v2/admin.go | 10 +- tools/data-conversion/chat/v2/chat.go | 16 +-- tools/data-conversion/chat/v3/admin/admin.go | 2 +- .../chat/v3/admin/client_config.go | 2 +- .../chat/v3/admin/forbidden_account.go | 2 +- .../chat/v3/admin/invitation_register.go | 2 +- .../chat/v3/admin/ip_forbidden.go | 2 +- .../chat/v3/admin/limit_user_login_ip.go | 2 +- .../chat/v3/admin/register_add_friend.go | 2 +- .../chat/v3/admin/register_add_group.go | 2 +- tools/data-conversion/chat/v3/chat/account.go | 2 +- .../data-conversion/chat/v3/chat/attribute.go | 2 +- .../data-conversion/chat/v3/chat/register.go | 2 +- .../chat/v3/chat/user_login_record.go | 2 +- .../cmd/conversion-msg/conversion-msg.go | 21 ++- .../cmd/conversion-mysql/conversion-mysql.go | 34 +++-- .../data-conversion/openim/mysql/v3/friend.go | 61 ++++---- .../openim/mysql/v3/friend_request.go | 40 +++--- .../data-conversion/openim/mysql/v3/group.go | 5 +- tools/data-conversion/openim/mysql/v3/user.go | 25 +++- tools/formitychecker/checker/checker.go | 3 +- tools/infra/infra.go | 4 +- tools/url2im/main.go | 43 ++++-- 131 files changed, 1127 insertions(+), 875 deletions(-) mode change 100755 => 100644 pkg/common/tls/tls.go create mode 100644 test/codescan/main.go create mode 100644 test/e2e/page/chat_page.go create mode 100644 test/e2e/page/login_page.go delete mode 100644 test/e2e/web/.keep create mode 100644 test/e2e/web/Readme.md diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 6970e7d9c..b5f901d25 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -92,6 +92,7 @@ jobs: - name: Exec OpenIM API test run: | + sudo make test-api mkdir -p ./tmp touch ./tmp/test.md echo "# OpenIM Test" >> ./tmp/test.md @@ -104,6 +105,7 @@ jobs: - name: Exec OpenIM E2E Test run: | + sudo make test-e2e echo "" >> ./tmp/test.md echo "## OpenIM E2E Test" >> ./tmp/test.md echo "
Command Output for OpenIM E2E Test" >> ./tmp/test.md diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index bc1b094bf..c123566f1 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -74,6 +74,17 @@ jobs: echo $latest_tag > pkg/common/config/version continue-on-error: true + - name: Gen CHANGELOG file + run: | + current_tag=$(git describe --tags --abbrev=0) + version=$(echo "$current_tag" | sed -E 's/^v?([0-9]+)\.([0-9]+)\..*$/\1.\2/') + echo "OpenIM Version: $version" + make tools.install.git-chglog + cd CHANGELOG + git-chglog --tag-filter-pattern "v${version}.*" -o CHANGELOG-${version}.md + cd .. + continue-on-error: true + - name: Run unit test and get test coverage run: | make cover diff --git a/CHANGELOG/CHANGELOG-3.5.md b/CHANGELOG/CHANGELOG-3.5.md index ed9ba77c6..0a7f5bfa3 100644 --- a/CHANGELOG/CHANGELOG-3.5.md +++ b/CHANGELOG/CHANGELOG-3.5.md @@ -8,6 +8,21 @@ ## [Unreleased] + +## [v3.5.1-alpha.2] - 2024-01-26 + + +## [v3.5.0+15.d356f7a] - 2024-01-26 + + +## [v3.5.1-rc.1] - 2024-01-23 + + +## [v3.5.0+2.e0bd54f-3-g52f9fc209] - 2024-01-12 + + +## [v3.5.0+2.e0bd54f-1-g4ce6a0fa6] - 2024-01-12 + ## [v3.5.1-alpha.1] - 2024-01-09 @@ -59,7 +74,12 @@ - Merge branch 'tuoyun' -[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...HEAD +[Unreleased]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.2...HEAD +[v3.5.1-alpha.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+15.d356f7a...v3.5.1-alpha.2 +[v3.5.0+15.d356f7a]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.1...v3.5.0+15.d356f7a +[v3.5.1-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+2.e0bd54f-3-g52f9fc209...v3.5.1-rc.1 +[v3.5.0+2.e0bd54f-3-g52f9fc209]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+2.e0bd54f-1-g4ce6a0fa6...v3.5.0+2.e0bd54f-3-g52f9fc209 +[v3.5.0+2.e0bd54f-1-g4ce6a0fa6]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...v3.5.0+2.e0bd54f-1-g4ce6a0fa6 [v3.5.1-alpha.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0...v3.5.1-alpha.1 [v3.5.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.1...v3.5.0 [v3.5.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-bate.1...v3.5.1 diff --git a/Makefile b/Makefile index 4faf1c21d..89b9e4152 100644 --- a/Makefile +++ b/Makefile @@ -184,7 +184,7 @@ test-e2e: imports: @$(MAKE) go.imports -## clean: Remove all files that are created by building. ✨ +## clean: Delete all files created by the build, as well as all log files. ✨ .PHONY: clean clean: @$(MAKE) go.clean diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 34ada1d57..eeaa04d07 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -67,20 +67,22 @@ func run(port int, proPort int) error { // Determine whether zk is passed according to whether it is a clustered deployment client, err = kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) if err != nil { - return errs.Wrap(err, "register discovery err") + return err } if err = client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { - return errs.Wrap(err, "create rpc root nodes error") + return err } if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.Config.EncodeConfig()); err != nil { return err } + var ( netDone = make(chan struct{}, 1) netErr error ) + router := api.NewGinRouter(client, rdb) if config.Config.Prometheus.Enable { go func() { @@ -91,7 +93,6 @@ func run(port int, proPort int) error { netDone <- struct{}{} } }() - } var address string @@ -108,7 +109,6 @@ func run(port int, proPort int) error { if err != nil && err != http.ErrServerClosed { netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr)) netDone <- struct{}{} - } }() @@ -122,7 +122,7 @@ func run(port int, proPort int) error { util.SIGTERMExit() err := server.Shutdown(ctx) if err != nil { - return errs.Wrap(err, "shutdown err") + return errs.Wrap(err, "api shutdown err") } case <-netDone: close(netDone) diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go index e0539fa52..bd31ffdef 100644 --- a/cmd/openim-push/main.go +++ b/cmd/openim-push/main.go @@ -26,7 +26,7 @@ func main() { pushCmd.AddPortFlag() pushCmd.AddPrometheusPortFlag() if err := pushCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go index b526c3b86..992a2b432 100644 --- a/cmd/openim-rpc/openim-rpc-auth/main.go +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -26,7 +26,7 @@ func main() { authCmd.AddPortFlag() authCmd.AddPrometheusPortFlag() if err := authCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go index bde191c51..10fe0b46c 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/main.go +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go index 8eeb9c8e1..63de23293 100644 --- a/cmd/openim-rpc/openim-rpc-friend/main.go +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go index a5842ffd1..c0780acab 100644 --- a/cmd/openim-rpc/openim-rpc-group/main.go +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go index b3895a502..62bdff0a5 100644 --- a/cmd/openim-rpc/openim-rpc-msg/main.go +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go index 8f390bb6a..c2893a398 100644 --- a/cmd/openim-rpc/openim-rpc-third/main.go +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { util.ExitWithError(err) diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go index 6994ea2b1..f7948bda0 100644 --- a/cmd/openim-rpc/openim-rpc-user/main.go +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -26,7 +26,7 @@ func main() { rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { - panic(err.Error()) + util.ExitWithError(err) } if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { util.ExitWithError(err) diff --git a/docs/contrib/code-conventions.md b/docs/contrib/code-conventions.md index 049df6381..6b534779b 100644 --- a/docs/contrib/code-conventions.md +++ b/docs/contrib/code-conventions.md @@ -3,7 +3,15 @@ - [Code conventions](#code-conventions) - [POSIX shell](#posix-shell) - [Go](#go) - - [Directory and file conventions](#directory-and-file-conventions) + - [OpenIM Naming Conventions Guide](#openim-naming-conventions-guide) + - [1. General File Naming](#1-general-file-naming) + - [2. Special File Types](#2-special-file-types) + - [a. Script and Markdown Files](#a-script-and-markdown-files) + - [b. Uppercase Markdown Documentation](#b-uppercase-markdown-documentation) + - [3. Directory Naming](#3-directory-naming) + - [4. Configuration Files](#4-configuration-files) + - [Best Practices](#best-practices) + - [Directory and File Conventions](#directory-and-file-conventions) - [Testing conventions](#testing-conventions) ## POSIX shell @@ -67,12 +75,13 @@ Files within the OpenIM project should adhere to the following rules: + Stick to lowercase naming where possible for consistency and to prevent issues with case-sensitive systems. + Include version numbers or dates in file names if the file is subject to updates, following the format: `project-plan-v1.2.md` or `backup-2023-03-15.sql`. -## Directory and file conventions +## Directory and File Conventions + +- Avoid generic utility packages. Instead of naming a package "util", choose a name that clearly describes its purpose. For instance, functions related to waiting operations are contained within the `wait` package, which includes methods like `Poll`, fully named as `wait.Poll`. +- All filenames, script files, configuration files, and directories should be in lowercase and use dashes (`-`) as separators. +- For Go language files, filenames should be in lowercase and use underscores (`_`). +- Package names should match their directory names to ensure consistency. For example, within the `openim-api` directory, the Go file should be named `openim-api.go`, following the convention of using dashes for directory names and aligning package names with directory names. -- Avoid general utility packages. Packages called "util" are suspect. Instead, derive a name that describes your desired function. For example, the utility functions dealing with waiting for operations are in the `wait` package and include functionality like `Poll`. The full name is `wait.Poll`. -- All filenames should be lowercase. -- All source files and directories should use underscores, not dashes. - - Package directories should generally avoid using separators as much as possible. When package names are multiple words, they usually should be in nested subdirectories. ## Testing conventions diff --git a/internal/api/custom_validator.go b/internal/api/custom_validator.go index 8c5890501..1df4169e4 100644 --- a/internal/api/custom_validator.go +++ b/internal/api/custom_validator.go @@ -20,19 +20,16 @@ import ( "github.com/OpenIMSDK/protocol/constant" ) +// RequiredIf validates if the specified field is required based on the session type. func RequiredIf(fl validator.FieldLevel) bool { sessionType := fl.Parent().FieldByName("SessionType").Int() + switch sessionType { case constant.SingleChatType, constant.NotificationChatType: - if fl.FieldName() == "RecvID" { - return fl.Field().String() != "" - } + return fl.FieldName() != "RecvID" || fl.Field().String() != "" case constant.GroupChatType, constant.SuperGroupChatType: - if fl.FieldName() == "GroupID" { - return fl.Field().String() != "" - } + return fl.FieldName() != "GroupID" || fl.Field().String() != "" default: return true } - return true } diff --git a/internal/api/msg.go b/internal/api/msg.go index 9348596ac..1bbcf5428 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -210,7 +210,6 @@ func (m *MessageApi) SendMessage(c *gin.Context) { sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg) if err != nil { // Log and respond with an error if preparation fails. - log.ZError(c, "decodeData failed", err) apiresp.GinError(c, err) return } @@ -226,7 +225,6 @@ func (m *MessageApi) SendMessage(c *gin.Context) { if err != nil { // Set the status to failed and respond with an error if sending fails. status = constant.MsgSendFailed - log.ZError(c, "send message err", err) apiresp.GinError(c, err) return } @@ -240,7 +238,8 @@ func (m *MessageApi) SendMessage(c *gin.Context) { }) if err != nil { // Log the error if updating the status fails. - log.ZError(c, "SetSendMsgStatus failed", err) + apiresp.GinError(c, err) + return } // Respond with a success message and the response payload. @@ -299,7 +298,6 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { resp apistruct.BatchSendMsgResp ) if err := c.BindJSON(&req); err != nil { - log.ZError(c, "BatchSendMsg BindJSON failed", err) apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } @@ -310,14 +308,12 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { } var recvIDs []string - var err error if req.IsSendAll { pageNumber := 1 showNumber := 500 for { recvIDsPart, err := m.userRpcClient.GetAllUserIDs(c, int32(pageNumber), int32(showNumber)) if err != nil { - log.ZError(c, "GetAllUserIDs failed", err) apiresp.GinError(c, err) return } @@ -333,7 +329,6 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { log.ZDebug(c, "BatchSendMsg nums", "nums ", len(recvIDs)) sendMsgReq, err := m.getSendMsgReq(c, req.SendMsg) if err != nil { - log.ZError(c, "decodeData failed", err) apiresp.GinError(c, err) return } diff --git a/internal/api/route.go b/internal/api/route.go index 24ed5f6bb..a6d73b683 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -44,7 +44,7 @@ import ( ) func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine { - discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) // 默认RPC中间件 + discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) // Default RPC middleware gin.SetMode(gin.ReleaseMode) r := gin.New() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { @@ -225,6 +225,7 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive return r } +// GinParseToken is a middleware that parses the token in the request header and verifies it. func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { dataBase := controller.NewAuthDatabase( cache.NewMsgCacheModel(rdb), @@ -250,13 +251,11 @@ func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { } m, err := dataBase.GetTokensWithoutError(c, claims.UserID, claims.PlatformID) if err != nil { - log.ZWarn(c, "cache get token error", errs.ErrTokenNotExist.Wrap()) apiresp.GinError(c, errs.ErrTokenNotExist.Wrap()) c.Abort() return } if len(m) == 0 { - log.ZWarn(c, "cache do not exist token error", errs.ErrTokenNotExist.Wrap()) apiresp.GinError(c, errs.ErrTokenNotExist.Wrap()) c.Abort() return @@ -265,12 +264,10 @@ func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { switch v { case constant.NormalToken: case constant.KickedToken: - log.ZWarn(c, "cache kicked token error", errs.ErrTokenKicked.Wrap()) apiresp.GinError(c, errs.ErrTokenKicked.Wrap()) c.Abort() return default: - log.ZWarn(c, "cache unknown token error", errs.ErrTokenUnknown.Wrap()) apiresp.GinError(c, errs.ErrTokenUnknown.Wrap()) c.Abort() return @@ -286,3 +283,10 @@ func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { } } } + +// // handleGinError logs and returns an error response through Gin context. +// func handleGinError(c *gin.Context, logMessage string, errType errs.CodeError, detail string) { +// wrappedErr := errType.Wrap(detail) +// apiresp.GinError(c, wrappedErr) +// c.Abort() +// } diff --git a/internal/api/user.go b/internal/api/user.go index e7bbd4bfb..901998319 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -68,7 +68,7 @@ func (u *UserApi) GetUsers(c *gin.Context) { func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { var req msggateway.GetUsersOnlineStatusReq if err := c.BindJSON(&req); err != nil { - apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) + apiresp.GinError(c, err) return } conns, err := u.Discov.GetConns(c, config.Config.RpcRegisterName.OpenImMessageGatewayName) @@ -86,7 +86,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { msgClient := msggateway.NewMsgGatewayClient(v) reply, err := msgClient.GetUsersOnlineStatus(c, &req) if err != nil { - log.ZWarn(c, "GetUsersOnlineStatus rpc err", err) + log.ZDebug(c, "GetUsersOnlineStatus rpc error", err) parseError := apiresp.ParseError(err) if parseError.ErrCode == errs.NoPermissionError { diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 9a4005e6c..06efea12f 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -91,13 +91,7 @@ type Client struct { // } // ResetClient updates the client's state with new connection and context information. -func (c *Client) ResetClient( - ctx *UserConnContext, - conn LongConn, - isBackground, isCompress bool, - longConnServer LongConnServer, - token string, -) { +func (c *Client) ResetClient(ctx *UserConnContext, conn LongConn, isBackground, isCompress bool, longConnServer LongConnServer, token string) { c.w = new(sync.Mutex) c.conn = conn c.PlatformID = utils.StringToInt(ctx.GetPlatformID()) @@ -112,9 +106,11 @@ func (c *Client) ResetClient( c.token = token } -// pingHandler handles ping messages and sends pong responses. func (c *Client) pingHandler(_ string) error { - _ = c.conn.SetReadDeadline(pongWait) + if err := c.conn.SetReadDeadline(pongWait); err != nil { + return err + } + return c.writePongMsg() } @@ -141,7 +137,8 @@ func (c *Client) readMessage() { } log.ZDebug(c.ctx, "readMessage", "messageType", messageType) - if c.closed.Load() { // 连接刚置位已经关闭,但是协程还没退出的场景 + if c.closed.Load() { + // The scenario where the connection has just been closed, but the coroutine has not exited c.closedErr = ErrConnClosed return } @@ -185,11 +182,11 @@ func (c *Client) handleMessage(message []byte) error { err := c.longConnServer.Decode(message, binaryReq) if err != nil { - return errs.Wrap(err) + return err } if err := c.longConnServer.Validate(binaryReq); err != nil { - return errs.Wrap(err) + return err } if binaryReq.SendID != c.UserID { @@ -239,7 +236,7 @@ func (c *Client) setAppBackgroundStatus(ctx context.Context, req *Req) ([]byte, } c.IsBackground = isBackground - // todo callback + // TODO: callback return resp, nil } @@ -273,7 +270,7 @@ func (c *Client) replyMessage(ctx context.Context, binaryReq *Req, err error, re } if binaryReq.ReqIdentifier == WsLogoutMsg { - return errors.New("user logout") + return errs.Wrap(errors.New("user logout")) } return nil } @@ -316,17 +313,21 @@ func (c *Client) writeBinaryMsg(resp Resp) error { encodedBuf, err := c.longConnServer.Encode(resp) if err != nil { - return errs.Wrap(err) + return err } c.w.Lock() defer c.w.Unlock() - _ = c.conn.SetWriteDeadline(writeWait) + err = c.conn.SetWriteDeadline(writeWait) + if err != nil { + return err + } + if c.IsCompress { resultBuf, compressErr := c.longConnServer.CompressWithPool(encodedBuf) if compressErr != nil { - return errs.Wrap(compressErr) + return compressErr } return c.conn.WriteMessage(MessageBinary, resultBuf) } @@ -344,7 +345,7 @@ func (c *Client) writePongMsg() error { err := c.conn.SetWriteDeadline(writeWait) if err != nil { - return errs.Wrap(err) + return err } return c.conn.WriteMessage(PongMessage, nil) diff --git a/internal/msggateway/compressor.go b/internal/msggateway/compressor.go index d4789536e..140aac4d8 100644 --- a/internal/msggateway/compressor.go +++ b/internal/msggateway/compressor.go @@ -17,7 +17,6 @@ package msggateway import ( "bytes" "compress/gzip" - "errors" "io" "sync" @@ -46,12 +45,15 @@ func NewGzipCompressor() *GzipCompressor { func (g *GzipCompressor) Compress(rawData []byte) ([]byte, error) { gzipBuffer := bytes.Buffer{} gz := gzip.NewWriter(&gzipBuffer) + if _, err := gz.Write(rawData); err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, "GzipCompressor.Compress: writing to gzip writer failed") } + if err := gz.Close(); err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, "GzipCompressor.Compress: closing gzip writer failed") } + return gzipBuffer.Bytes(), nil } @@ -63,10 +65,10 @@ func (g *GzipCompressor) CompressWithPool(rawData []byte) ([]byte, error) { gz.Reset(&gzipBuffer) if _, err := gz.Write(rawData); err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error writing data") } if err := gz.Close(); err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, "GzipCompressor.CompressWithPool: error closing gzip writer") } return gzipBuffer.Bytes(), nil } @@ -75,32 +77,36 @@ func (g *GzipCompressor) DeCompress(compressedData []byte) ([]byte, error) { buff := bytes.NewBuffer(compressedData) reader, err := gzip.NewReader(buff) if err != nil { - return nil, errs.Wrap(err, "NewReader failed") + return nil, errs.Wrap(err, "GzipCompressor.DeCompress: NewReader creation failed") } - compressedData, err = io.ReadAll(reader) + decompressedData, err := io.ReadAll(reader) if err != nil { - return nil, errs.Wrap(err, "ReadAll failed") + return nil, errs.Wrap(err, "GzipCompressor.DeCompress: reading from gzip reader failed") } - _ = reader.Close() - return compressedData, nil + if err = reader.Close(); err != nil { + // Even if closing the reader fails, we've successfully read the data, + // so we return the decompressed data and an error indicating the close failure. + return decompressedData, errs.Wrap(err, "GzipCompressor.DeCompress: closing gzip reader failed") + } + return decompressedData, nil } func (g *GzipCompressor) DecompressWithPool(compressedData []byte) ([]byte, error) { reader := gzipReaderPool.Get().(*gzip.Reader) - if reader == nil { - return nil, errs.Wrap(errors.New("NewReader failed")) - } defer gzipReaderPool.Put(reader) err := reader.Reset(bytes.NewReader(compressedData)) if err != nil { - return nil, errs.Wrap(err, "NewReader failed") + return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: resetting gzip reader failed") } - compressedData, err = io.ReadAll(reader) + decompressedData, err := io.ReadAll(reader) if err != nil { - return nil, errs.Wrap(err, "ReadAll failed") + return nil, errs.Wrap(err, "GzipCompressor.DecompressWithPool: reading from pooled gzip reader failed") + } + if err = reader.Close(); err != nil { + // Similar to DeCompress, return the data and error for close failure. + return decompressedData, errs.Wrap(err, "GzipCompressor.DecompressWithPool: closing pooled gzip reader failed") } - _ = reader.Close() - return compressedData, nil + return decompressedData, nil } diff --git a/internal/msggateway/compressor_test.go b/internal/msggateway/compressor_test.go index b1544f063..bb7106d9f 100644 --- a/internal/msggateway/compressor_test.go +++ b/internal/msggateway/compressor_test.go @@ -37,10 +37,16 @@ func TestCompressDecompress(t *testing.T) { // compress dest, err := compressor.CompressWithPool(src) + if err != nil { + t.Log(err) + } assert.Equal(t, nil, err) // decompress res, err := compressor.DecompressWithPool(dest) + if err != nil { + t.Log(err) + } assert.Equal(t, nil, err) // check @@ -60,10 +66,16 @@ func TestCompressDecompressWithConcurrency(t *testing.T) { // compress dest, err := compressor.CompressWithPool(src) + if err != nil { + t.Log(err) + } assert.Equal(t, nil, err) // decompress res, err := compressor.DecompressWithPool(dest) + if err != nil { + t.Log(err) + } assert.Equal(t, nil, err) // check @@ -99,6 +111,7 @@ func BenchmarkDecompress(b *testing.B) { compressor := NewGzipCompressor() comdata, err := compressor.Compress(src) + assert.Equal(b, nil, err) for i := 0; i < b.N; i++ { diff --git a/internal/msggateway/encoder.go b/internal/msggateway/encoder.go index 69a899591..cd2c50d96 100644 --- a/internal/msggateway/encoder.go +++ b/internal/msggateway/encoder.go @@ -37,7 +37,7 @@ func (g *GobEncoder) Encode(data any) ([]byte, error) { enc := gob.NewEncoder(&buff) err := enc.Encode(data) if err != nil { - return nil, err + return nil, errs.Wrap(err, "GobEncoder.Encode failed") } return buff.Bytes(), nil } @@ -47,7 +47,7 @@ func (g *GobEncoder) Decode(encodeData []byte, decodeData any) error { dec := gob.NewDecoder(buff) err := dec.Decode(decodeData) if err != nil { - return errs.Wrap(err) + return errs.Wrap(err, "GobEncoder.Decode failed") } return nil } diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index b18efcd50..5d19ad16d 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -23,14 +23,7 @@ import ( // RunWsAndServer run ws server. func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error { - fmt.Println( - "start rpc/msg_gateway server, port: ", - rpcPort, - wsPort, - prometheusPort, - ", OpenIM version: ", - config.Version, - ) + fmt.Println("start rpc/msg_gateway server, port: ", rpcPort, wsPort, prometheusPort, ", OpenIM version: ", config.Version) longServer, err := NewWsServer( WithPort(wsPort), WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)), diff --git a/internal/msggateway/long_conn.go b/internal/msggateway/long_conn.go index a4251a50f..7dc79c834 100644 --- a/internal/msggateway/long_conn.go +++ b/internal/msggateway/long_conn.go @@ -15,9 +15,11 @@ package msggateway import ( + "errors" "net/http" "time" + "github.com/OpenIMSDK/tools/errs" "github.com/gorilla/websocket" ) @@ -72,7 +74,8 @@ func (d *GWebSocket) GenerateLongConn(w http.ResponseWriter, r *http.Request) er conn, err := upgrader.Upgrade(w, r, nil) if err != nil { - return err + // The upgrader.Upgrade method usually returns enough error messages to diagnose problems that may occur during the upgrade + return errs.Wrap(err, "GenerateLongConn: WebSocket upgrade failed") } d.conn = conn return nil @@ -96,7 +99,16 @@ func (d *GWebSocket) SetReadDeadline(timeout time.Duration) error { } func (d *GWebSocket) SetWriteDeadline(timeout time.Duration) error { - return d.conn.SetWriteDeadline(time.Now().Add(timeout)) + // TODO add error + if timeout <= 0 { + return errs.Wrap(errors.New("timeout must be greater than 0")) + } + + // TODO SetWriteDeadline Future add error handling + if err := d.conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil { + return errs.Wrap(err, "GWebSocket.SetWriteDeadline failed") + } + return nil } func (d *GWebSocket) Dial(urlStr string, requestHeader http.Header) (*http.Response, error) { diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index dd5e00f18..3c5bd6121 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -20,6 +20,7 @@ import ( "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" "github.com/go-playground/validator/v10" "google.golang.org/protobuf/proto" @@ -119,10 +120,10 @@ func NewGrpcHandler(validate *validator.Validate, client discoveryregistry.SvcDi func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error) { req := sdkws.GetMaxSeqReq{} if err := proto.Unmarshal(data.Data, &req); err != nil { - return nil, err + return nil, errs.Wrap(err, "GetSeq: error unmarshaling request") } if err := g.validate.Struct(&req); err != nil { - return nil, err + return nil, errs.Wrap(err, "GetSeq: validation failed") } resp, err := g.msgRpcClient.GetMaxSeq(context, &req) if err != nil { @@ -130,28 +131,37 @@ func (g GrpcHandler) GetSeq(context context.Context, data *Req) ([]byte, error) } c, err := proto.Marshal(resp) if err != nil { - return nil, err + return nil, errs.Wrap(err, "GetSeq: error marshaling response") } return c, nil } -func (g GrpcHandler) SendMessage(context context.Context, data *Req) ([]byte, error) { - msgData := sdkws.MsgData{} +// SendMessage handles the sending of messages through gRPC. It unmarshals the request data, +// validates the message, and then sends it using the message RPC client. +func (g GrpcHandler) SendMessage(ctx context.Context, data *Req) ([]byte, error) { + // Unmarshal the message data from the request. + var msgData sdkws.MsgData if err := proto.Unmarshal(data.Data, &msgData); err != nil { - return nil, err + return nil, errs.Wrap(err, "error unmarshalling message data") } + + // Validate the message data structure. if err := g.validate.Struct(&msgData); err != nil { - return nil, err + return nil, errs.Wrap(err, "message data validation failed") } + req := msg.SendMsgReq{MsgData: &msgData} - resp, err := g.msgRpcClient.SendMsg(context, &req) + + resp, err := g.msgRpcClient.SendMsg(ctx, &req) if err != nil { return nil, err } + c, err := proto.Marshal(resp) if err != nil { - return nil, err + return nil, errs.Wrap(err, "error marshaling response") } + return c, nil } @@ -162,7 +172,7 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by } c, err := proto.Marshal(resp) if err != nil { - return nil, err + return nil, errs.Wrap(err, "error marshaling response") } return c, nil } @@ -170,7 +180,7 @@ func (g GrpcHandler) SendSignalMessage(context context.Context, data *Req) ([]by func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([]byte, error) { req := sdkws.PullMessageBySeqsReq{} if err := proto.Unmarshal(data.Data, &req); err != nil { - return nil, err + return nil, errs.Wrap(err, "error unmarshaling request") } if err := g.validate.Struct(data); err != nil { return nil, err diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index b734dee6d..f466a7dc4 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -88,6 +88,7 @@ type WsServer struct { Encoder MessageHandler } + type kickHandler struct { clientOK bool oldClients []*Client @@ -129,7 +130,9 @@ func (ws *WsServer) UnRegister(c *Client) { } func (ws *WsServer) Validate(s any) error { - //?question? + if s == nil { + return errs.Wrap(errors.New("input cannot be nil")) + } return nil } @@ -276,7 +279,7 @@ func (ws *WsServer) registerClient(client *Client) { log.ZDebug(client.ctx, "user exist", "userID", client.UserID, "platformID", client.PlatformID) if clientOK { ws.clients.Set(client.UserID, client) - // 已经有同平台的连接存在 + // There is already a connection to the platform log.ZInfo(client.ctx, "repeat login", "userID", client.UserID, "platformID", client.PlatformID, "old remote addr", getRemoteAdders(oldClients)) ws.onlineUserConnNum.Add(1) } else { diff --git a/internal/msggateway/options.go b/internal/msggateway/options.go index 6513ac5dc..b65f8e36c 100644 --- a/internal/msggateway/options.go +++ b/internal/msggateway/options.go @@ -19,15 +19,15 @@ import "time" type ( Option func(opt *configs) configs struct { - // 长连接监听端口 + // Long connection listening port port int - // 长连接允许最大链接数 + // Maximum number of connections allowed for long connection maxConnNum int64 - // 连接握手超时时间 + // Connection handshake timeout handshakeTimeout time.Duration - // 允许消息最大长度 + // Maximum length allowed for messages messageMaxMsgLength int - // websocket write buffer, default: 4096, 4kb. + // Websocket write buffer, default: 4096, 4kb. writeBufferSize int } ) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 9b1b853f2..b5f8516f8 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -45,8 +45,13 @@ import ( ) type MsgTransfer struct { - historyCH *OnlineHistoryRedisConsumerHandler // 这个消费者聚合消息, 订阅的topic:ws2ms_chat, 修改通知发往msg_to_modify topic, 消息存入redis后Incr Redis, 再发消息到ms2pschat topic推送, 发消息到msg_to_mongo topic持久化 - historyMongoCH *OnlineHistoryMongoConsumerHandler // mongoDB批量插入, 成功后删除redis中消息,以及处理删除通知消息删除的 订阅的topic: msg_to_mongo + // This consumer aggregated messages, subscribed to the topic:ws2ms_chat, + // the modification notification is sent to msg_to_modify topic, the message is stored in redis, Incr Redis, + // and then the message is sent to ms2pschat topic for push, and the message is sent to msg_to_mongo topic for persistence + historyCH *OnlineHistoryRedisConsumerHandler + // mongoDB batch insert, delete messages in redis after success, + // and handle the deletion notification message deleted subscriptions topic: msg_to_mongo + historyMongoCH *OnlineHistoryMongoConsumerHandler ctx context.Context cancel context.CancelFunc } @@ -65,6 +70,7 @@ func StartTransfer(prometheusPort int) error { if err = mongo.CreateMsgIndex(); err != nil { return err } + client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) if err != nil { return err @@ -73,6 +79,7 @@ func StartTransfer(prometheusPort int) error { if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { return err } + client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) msgModel := cache.NewMsgCacheModel(rdb) msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) @@ -106,7 +113,7 @@ func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcCli } func (m *MsgTransfer) Start(prometheusPort int) error { - fmt.Println("start msg transfer", "prometheusPort:", prometheusPort) + fmt.Println("Start msg transfer", "prometheusPort:", prometheusPort) if prometheusPort <= 0 { return errs.Wrap(errors.New("prometheusPort not correct")) } diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 393ec7a75..5023ac008 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -155,21 +155,13 @@ func (och *OnlineHistoryRedisConsumerHandler) Run(channelID int) { notStorageNotificationList, ) if err := och.msgDatabase.MsgToModifyMQ(ctx, msgChannelValue.uniqueKey, conversationIDNotification, modifyMsgList); err != nil { - log.ZError( - ctx, - "msg to modify mq error", - err, - "uniqueKey", - msgChannelValue.uniqueKey, - "modifyMsgList", - modifyMsgList, - ) + log.ZError(ctx, "msg to modify mq error", err, "uniqueKey", msgChannelValue.uniqueKey, "modifyMsgList", modifyMsgList) } } } } -// 获取消息/通知 存储的消息列表, 不存储并且推送的消息列表,. +// Get messages/notifications stored message list, not stored and pushed message list. func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList( totalMsgs []*ContextMsg, ) (storageMsgList, notStorageMsgList, storageNotificatoinList, notStorageNotificationList, modifyMsgList []*sdkws.MsgData) { @@ -190,7 +182,7 @@ func (och *OnlineHistoryRedisConsumerHandler) getPushStorageMsgList( // clone msg from notificationMsg if options.IsSendMsg() { msg := proto.Clone(v.message).(*sdkws.MsgData) - // 消息 + // message if v.message.Options != nil { msg.Options = msgprocessor.NewMsgOptions() } diff --git a/internal/push/callback.go b/internal/push/callback.go index a572fa572..46b5545fb 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -31,12 +31,7 @@ func url() string { return config.Config.Callback.CallbackUrl } -func callbackOfflinePush( - ctx context.Context, - userIDs []string, - msg *sdkws.MsgData, - offlinePushUserIDs *[]string, -) error { +func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error { if !config.Config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing { return nil } @@ -59,10 +54,12 @@ func callbackOfflinePush( AtUserIDs: msg.AtUserIDList, Content: GetContent(msg), } + resp := &callbackstruct.CallbackBeforePushResp{} if err := http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOfflinePush); err != nil { return err } + if len(resp.UserIDs) != 0 { *offlinePushUserIDs = resp.UserIDs } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 8145d4c17..a60570860 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -39,20 +39,22 @@ type Fcm struct { cache cache.MsgModel } +// NewClient initializes a new FCM client using the Firebase Admin SDK. +// It requires the FCM service account credentials file located within the project's configuration directory. func NewClient(cache cache.MsgModel) *Fcm { - projectRoot := config.GetProjectRoot() + projectRoot, _ := config.GetProjectRoot() credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount) opt := option.WithCredentialsFile(credentialsFilePath) fcmApp, err := firebase.NewApp(context.Background(), nil, opt) if err != nil { return nil } - ctx := context.Background() fcmMsgClient, err := fcmApp.Messaging(ctx) if err != nil { return nil } + return &Fcm{fcmMsgCli: fcmMsgClient, cache: cache} } diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 5fce34e83..1140e8ce4 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -229,7 +229,8 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws }(groupID, kickedUsers) pushToUserIDs = append(pushToUserIDs, kickedUsers...) case constant.GroupDismissedNotification: - if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { // 消息先到,通知后到 + // Messages arrive first, notifications arrive later + if msgprocessor.IsNotification(msgprocessor.GetConversationIDByMsg(msg)) { var tips sdkws.GroupDismissedTips if p.UnmarshalNotificationElem(msg.Content, &tips) != nil { return err diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 8558a23ea..582f93246 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -310,7 +310,7 @@ func (c *conversationServer) SetConversations(ctx context.Context, unequal++ } } - if err := c.conversationDatabase.SetUsersConversationFiledTx(ctx, req.UserIDs, &conversation, m); err != nil { + if err := c.conversationDatabase.SetUsersConversationFieldTx(ctx, req.UserIDs, &conversation, m); err != nil { return nil, err } if unequal > 0 { @@ -321,7 +321,7 @@ func (c *conversationServer) SetConversations(ctx context.Context, return &pbconversation.SetConversationsResp{}, nil } -// 获取超级大群开启免打扰的用户ID. +// Get user IDs with "Do Not Disturb" enabled in super large groups. func (c *conversationServer) GetRecvMsgNotNotifyUserIDs(ctx context.Context, req *pbconversation.GetRecvMsgNotNotifyUserIDsReq) (*pbconversation.GetRecvMsgNotNotifyUserIDsResp, error) { //userIDs, err := c.conversationDatabase.FindRecvMsgNotNotifyUserIDs(ctx, req.GroupID) //if err != nil { @@ -378,7 +378,7 @@ func (c *conversationServer) CreateGroupChatConversations(ctx context.Context, r } func (c *conversationServer) SetConversationMaxSeq(ctx context.Context, req *pbconversation.SetConversationMaxSeqReq) (*pbconversation.SetConversationMaxSeqResp, error) { - if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, req.OwnerUserID, req.ConversationID, + if err := c.conversationDatabase.UpdateUsersConversationField(ctx, req.OwnerUserID, req.ConversationID, map[string]any{"max_seq": req.MaxSeq}); err != nil { return nil, err } diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 84702f548..9c49af220 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -278,6 +278,7 @@ func (s *friendServer) GetDesignatedFriends(ctx context.Context, req *pbfriend.G return resp, nil } +// Get the list of friend requests sent out proactively. func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context, req *pbfriend.GetDesignatedFriendsApplyReq) (resp *pbfriend.GetDesignatedFriendsApplyResp, err error) { friendRequests, err := s.friendDatabase.FindBothFriendRequests(ctx, req.FromUserID, req.ToUserID) @@ -292,7 +293,7 @@ func (s *friendServer) GetDesignatedFriendsApply(ctx context.Context, return resp, nil } -// ok 获取接收到的好友申请(即别人主动申请的). +// Get received friend requests (i.e., those initiated by others). func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyToReq) (resp *pbfriend.GetPaginationFriendsApplyToResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") if err := s.userRpcClient.Access(ctx, req.UserID); err != nil { @@ -311,7 +312,6 @@ func (s *friendServer) GetPaginationFriendsApplyTo(ctx context.Context, req *pbf return resp, nil } -// ok 获取主动发出去的好友申请列表. func (s *friendServer) GetPaginationFriendsApplyFrom(ctx context.Context, req *pbfriend.GetPaginationFriendsApplyFromReq) (resp *pbfriend.GetPaginationFriendsApplyFromResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.GetPaginationFriendsApplyFromResp{} diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 9b1198d58..60f6c3eb5 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -765,8 +765,8 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed") } var inGroup bool - if _, err := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil { - inGroup = true // 已经在群里了 + if _, takeErr := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); takeErr == nil { + inGroup = true // Already in group } else if !s.IsNotFound(err) { return nil, err } diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 7a63d3526..0d474d2c4 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -67,7 +67,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e if err != nil { return err } - // 根据配置文件策略选择 oss 方式 + // Select based on the configuration file strategy enable := config.Config.Object.Enable var o s3.Interface switch config.Config.Object.Enable { diff --git a/internal/tools/conversation.go b/internal/tools/conversation.go index 0d0275339..6b918c0d1 100644 --- a/internal/tools/conversation.go +++ b/internal/tools/conversation.go @@ -58,9 +58,9 @@ import ( // continue // } // if len(seqs) > 0 { -// if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": now}); err +// if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]interface{}{"latest_msg_destruct_time": now}); err // != nil { -// log.ZError(ctx, "updateUsersConversationFiled failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) +// log.ZError(ctx, "updateUsersConversationField failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) // continue // } // if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil { @@ -139,8 +139,8 @@ func (c *MsgTool) ConversationsDestructMsgs() { continue } if len(seqs) > 0 { - if err := c.conversationDatabase.UpdateUsersConversationFiled(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]any{"latest_msg_destruct_time": now}); err != nil { - log.ZError(ctx, "updateUsersConversationFiled failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) + if err := c.conversationDatabase.UpdateUsersConversationField(ctx, []string{conversation.OwnerUserID}, conversation.ConversationID, map[string]any{"latest_msg_destruct_time": now}); err != nil { + log.ZError(ctx, "updateUsersConversationField failed", err, "conversationID", conversation.ConversationID, "ownerUserID", conversation.OwnerUserID) continue } if err := c.msgNotificationSender.UserDeleteMsgsNotification(ctx, conversation.OwnerUserID, conversation.ConversationID, seqs); err != nil { diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index decc1aa82..0544f2f85 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -32,7 +32,7 @@ import ( ) func StartTask() error { - fmt.Println("cron task start, config", config.Config.ChatRecordsClearTime) + fmt.Println("Cron task start, config:", config.Config.ChatRecordsClearTime) msgTool, err := InitMsgTool() if err != nil { @@ -48,16 +48,16 @@ func StartTask() error { // register cron tasks var crontab = cron.New() - fmt.Println("start chatRecordsClearTime cron task", "cron config", config.Config.ChatRecordsClearTime) + fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.Config.ChatRecordsClearTime) _, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) if err != nil { return errs.Wrap(err) } - fmt.Println("start msgDestruct cron task", "cron config", config.Config.MsgDestructTime) + fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.Config.MsgDestructTime) _, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) if err != nil { - return errs.Wrap(err) + return errs.Wrap(err, "cron_conversations_destruct_msgs") } // start crontab diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 1ec1e03a2..7548e3c04 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -197,7 +197,8 @@ func (c *MsgTool) checkMaxSeqWithMongo(ctx context.Context, conversationID strin return err } if math.Abs(float64(maxSeqMongo-maxSeqCache)) > 10 { - log.ZError(ctx, "cache max seq and mongo max seq is diff > 10", nil, "maxSeqMongo", maxSeqMongo, "minSeqMongo", minSeqMongo, "maxSeqCache", maxSeqCache, "conversationID", conversationID) + err = fmt.Errorf("cache max seq and mongo max seq is diff > 10, maxSeqMongo:%d,minSeqMongo:%d,maxSeqCache:%d,conversationID:%s", maxSeqMongo, minSeqMongo, maxSeqCache, conversationID) + return errs.Wrap(err) } return nil } @@ -219,7 +220,6 @@ func (c *MsgTool) checkMaxSeq(ctx context.Context, conversationID string) error func (c *MsgTool) FixAllSeq(ctx context.Context) error { conversationIDs, err := c.conversationDatabase.GetAllConversationIDs(ctx) if err != nil { - log.ZError(ctx, "GetAllConversationIDs failed", err) return err } for _, conversationID := range conversationIDs { diff --git a/pkg/apistruct/msg.go b/pkg/apistruct/msg.go index d23db9bf5..d1ce427fc 100644 --- a/pkg/apistruct/msg.go +++ b/pkg/apistruct/msg.go @@ -67,6 +67,7 @@ type LocationElem struct { Longitude float64 `mapstructure:"longitude" validate:"required"` Latitude float64 `mapstructure:"latitude" validate:"required"` } + type CustomElem struct { Data string `mapstructure:"data" validate:"required"` Description string `mapstructure:"description"` diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index 97bb03391..cfba2d844 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -44,7 +44,7 @@ func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { if opUserID == ownerUserID { return nil } - return errs.ErrNoPermission.Wrap(utils.GetSelfFuncName()) + return errs.Wrap(errs.ErrNoPermission, "CheckAccessV3: no permission for user "+opUserID) } func IsAppManagerUid(ctx context.Context) bool { @@ -61,6 +61,7 @@ func CheckAdmin(ctx context.Context) error { } return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) } + func CheckIMAdmin(ctx context.Context) error { if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) { return nil diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index db1f488ad..7156ce6c4 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -15,6 +15,9 @@ package cmd import ( + "errors" + "fmt" + "github.com/OpenIMSDK/protocol/constant" "github.com/spf13/cobra" @@ -32,17 +35,35 @@ func NewApiCmd() *ApiCmd { return ret } +// AddApi configures the API command to run with specified ports for the API and Prometheus monitoring. +// It ensures error handling for port retrieval and only proceeds if both port numbers are successfully obtained. func (a *ApiCmd) AddApi(f func(port int, promPort int) error) { a.Command.RunE = func(cmd *cobra.Command, args []string) error { - return f(a.getPortFlag(cmd), a.getPrometheusPortFlag(cmd)) + port, err := a.getPortFlag(cmd) + if err != nil { + return err + } + + promPort, err := a.getPrometheusPortFlag(cmd) + if err != nil { + return err + } + + return f(port, promPort) } } -func (a *ApiCmd) GetPortFromConfig(portType string) int { +func (a *ApiCmd) GetPortFromConfig(portType string) (int, error) { if portType == constant.FlagPort { - return config2.Config.Api.OpenImApiPort[0] + if len(config2.Config.Api.OpenImApiPort) > 0 { + return config2.Config.Api.OpenImApiPort[0], nil + } + return 0, errors.New("API port configuration is empty or missing") } else if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.ApiPrometheusPort[0] + if len(config2.Config.Prometheus.ApiPrometheusPort) > 0 { + return config2.Config.Prometheus.ApiPrometheusPort[0], nil + } + return 0, errors.New("Prometheus port configuration is empty or missing") } - return 0 + return 0, fmt.Errorf("unknown port type: %s", portType) } diff --git a/pkg/common/cmd/cron_task.go b/pkg/common/cmd/cron_task.go index 1b0e796ac..fa7a46351 100644 --- a/pkg/common/cmd/cron_task.go +++ b/pkg/common/cmd/cron_task.go @@ -36,3 +36,7 @@ func (c *CronTaskCmd) Exec(f func() error) error { c.addRunE(f) return c.Execute() } + +func (c *CronTaskCmd) GetPortFromConfig(portType string) (int, error) { + return 0, nil +} diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 25fcc1177..317844770 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -15,13 +15,15 @@ package cmd import ( - "log" + "errors" "github.com/spf13/cobra" + "github.com/openimsdk/open-im-server/v3/internal/msggateway" + "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/internal/msggateway" v3config "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) @@ -39,20 +41,32 @@ func (m *MsgGatewayCmd) AddWsPortFlag() { m.Command.Flags().IntP(constant.FlagWsPort, "w", 0, "ws server listen port") } -func (m *MsgGatewayCmd) getWsPortFlag(cmd *cobra.Command) int { +func (m *MsgGatewayCmd) getWsPortFlag(cmd *cobra.Command) (int, error) { port, err := cmd.Flags().GetInt(constant.FlagWsPort) if err != nil { - log.Println("Error getting ws port flag:", err) + return 0, errs.Wrap(err, "error getting ws port flag") } if port == 0 { - port = m.PortFromConfig(constant.FlagWsPort) + port, _ = m.PortFromConfig(constant.FlagWsPort) } - return port + return port, nil } func (m *MsgGatewayCmd) addRunE() { m.Command.RunE = func(cmd *cobra.Command, args []string) error { - return msggateway.RunWsAndServer(m.getPortFlag(cmd), m.getWsPortFlag(cmd), m.getPrometheusPortFlag(cmd)) + wsPort, err := m.getWsPortFlag(cmd) + if err != nil { + return errs.Wrap(err, "failed to get WS port flag") + } + port, err := m.getPortFlag(cmd) + if err != nil { + return err + } + prometheusPort, err := m.getPrometheusPortFlag(cmd) + if err != nil { + return err + } + return msggateway.RunWsAndServer(port, wsPort, prometheusPort) } } @@ -61,18 +75,33 @@ func (m *MsgGatewayCmd) Exec() error { return m.Execute() } -func (m *MsgGatewayCmd) GetPortFromConfig(portType string) int { +func (m *MsgGatewayCmd) GetPortFromConfig(portType string) (int, error) { + var port int + var exists bool + switch portType { case constant.FlagWsPort: - return v3config.Config.LongConnSvr.OpenImWsPort[0] + if len(v3config.Config.LongConnSvr.OpenImWsPort) > 0 { + port = v3config.Config.LongConnSvr.OpenImWsPort[0] + exists = true + } case constant.FlagPort: - return v3config.Config.LongConnSvr.OpenImMessageGatewayPort[0] + if len(v3config.Config.LongConnSvr.OpenImMessageGatewayPort) > 0 { + port = v3config.Config.LongConnSvr.OpenImMessageGatewayPort[0] + exists = true + } case constant.FlagPrometheusPort: - return v3config.Config.Prometheus.MessageGatewayPrometheusPort[0] + if len(v3config.Config.Prometheus.MessageGatewayPrometheusPort) > 0 { + port = v3config.Config.Prometheus.MessageGatewayPrometheusPort[0] + exists = true + } + } - default: - return 0 + if !exists { + return 0, errs.Wrap(errors.New("port type '%s' not found in configuration"), portType) } + + return port, nil } diff --git a/pkg/common/cmd/msg_gateway_test.go b/pkg/common/cmd/msg_gateway_test.go index c0ea2b057..106ad74ec 100644 --- a/pkg/common/cmd/msg_gateway_test.go +++ b/pkg/common/cmd/msg_gateway_test.go @@ -44,7 +44,7 @@ func TestMsgGatewayCmd_GetPortFromConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.portType, func(t *testing.T) { - got := msgGatewayCmd.GetPortFromConfig(tt.portType) + got, _ := msgGatewayCmd.GetPortFromConfig(tt.portType) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index e57bab89d..545c7543b 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -37,7 +37,11 @@ func NewMsgTransferCmd() *MsgTransferCmd { func (m *MsgTransferCmd) addRunE() { m.Command.RunE = func(cmd *cobra.Command, args []string) error { - return msgtransfer.StartTransfer(m.getPrometheusPortFlag(cmd)) + prometheusPort, err := m.getPrometheusPortFlag(cmd) + if err != nil { + return err + } + return msgtransfer.StartTransfer(prometheusPort) } } @@ -46,14 +50,18 @@ func (m *MsgTransferCmd) Exec() error { return m.Execute() } -func (m *MsgTransferCmd) GetPortFromConfig(portType string) int { +func (m *MsgTransferCmd) GetPortFromConfig(portType string) (int, error) { if portType == constant.FlagPort { - return 0 + return 0, nil } else if portType == constant.FlagPrometheusPort { n := m.getTransferProgressFlagValue() - return config2.Config.Prometheus.MessageTransferPrometheusPort[n] + + if n < len(config2.Config.Prometheus.MessageTransferPrometheusPort) { + return config2.Config.Prometheus.MessageTransferPrometheusPort[n], nil + } + return 0, fmt.Errorf("index out of range for MessageTransferPrometheusPort with index %d", n) } - return 0 + return 0, fmt.Errorf("unknown port type: %s", portType) } func (m *MsgTransferCmd) AddTransferProgressFlag() { diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index dd6d645d7..3c670ad85 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -18,6 +18,7 @@ import ( "github.com/spf13/cobra" "github.com/openimsdk/open-im-server/v3/internal/tools" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type MsgUtilsCmd struct { @@ -137,7 +138,7 @@ func (s *SeqCmd) GetSeqCmd() *cobra.Command { s.Command.Run = func(cmdLines *cobra.Command, args []string) { _, err := tools.InitMsgTool() if err != nil { - panic(err) + util.ExitWithError(err) } userID := s.getUserIDFlag(cmdLines) superGroupID := s.getSuperGroupIDFlag(cmdLines) diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index eab4a32bc..7256bd0ed 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -19,6 +19,8 @@ import ( config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/OpenIMSDK/tools/errs" + "github.com/spf13/cobra" "github.com/OpenIMSDK/protocol/constant" @@ -28,8 +30,9 @@ import ( ) type RootCmdPt interface { - GetPortFromConfig(portType string) int + GetPortFromConfig(portType string) (int, error) } + type RootCmd struct { Command cobra.Command Name string @@ -77,7 +80,7 @@ func (rc *RootCmd) persistentPreRun(cmd *cobra.Command, opts ...func(*CmdOpts)) cmdOpts := rc.applyOptions(opts...) if err := rc.initializeLogger(cmdOpts); err != nil { - return fmt.Errorf("failed to initialize from config: %w", err) + return errs.Wrap(err, "failed to initialize logger") } return nil @@ -130,31 +133,41 @@ func (r *RootCmd) AddPortFlag() { r.Command.Flags().IntP(constant.FlagPort, "p", 0, "server listen port") } -func (r *RootCmd) getPortFlag(cmd *cobra.Command) int { +func (r *RootCmd) getPortFlag(cmd *cobra.Command) (int, error) { port, err := cmd.Flags().GetInt(constant.FlagPort) if err != nil { - fmt.Println("Error getting ws port flag:", err) + // Wrapping the error with additional context + return 0, errs.Wrap(err, "error getting port flag") } if port == 0 { - port = r.PortFromConfig(constant.FlagPort) + port, _ = r.PortFromConfig(constant.FlagPort) + // port, err := r.PortFromConfig(constant.FlagPort) + // if err != nil { + // // Optionally wrap the error if it's an internal error needing context + // return 0, errs.Wrap(err, "error getting port from config") + // } } - return port + return port, nil } -func (r *RootCmd) GetPortFlag() int { - return r.port +// // GetPortFlag returns the port flag. +func (r *RootCmd) GetPortFlag() (int, error) { + return r.port, nil } func (r *RootCmd) AddPrometheusPortFlag() { r.Command.Flags().IntP(constant.FlagPrometheusPort, "", 0, "server prometheus listen port") } -func (r *RootCmd) getPrometheusPortFlag(cmd *cobra.Command) int { - port, _ := cmd.Flags().GetInt(constant.FlagPrometheusPort) - if port == 0 { - port = r.PortFromConfig(constant.FlagPrometheusPort) +func (r *RootCmd) getPrometheusPortFlag(cmd *cobra.Command) (int, error) { + port, err := cmd.Flags().GetInt(constant.FlagPrometheusPort) + if err != nil || port == 0 { + port, err = r.PortFromConfig(constant.FlagPrometheusPort) + if err != nil { + return 0, err + } } - return port + return port, nil } func (r *RootCmd) GetPrometheusPortFlag() int { @@ -175,10 +188,11 @@ func (r *RootCmd) AddCommand(cmds ...*cobra.Command) { r.Command.AddCommand(cmds...) } -func (r *RootCmd) GetPortFromConfig(portType string) int { - return 0 -} - -func (r *RootCmd) PortFromConfig(portType string) int { - return r.cmdItf.GetPortFromConfig(portType) +func (r *RootCmd) PortFromConfig(portType string) (int, error) { + // Retrieve the port and cache it + port, err := r.cmdItf.GetPortFromConfig(portType) + if err != nil { + return 0, err + } + return port, nil } diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index ea2a00b07..39bf9c036 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -16,11 +16,14 @@ package cmd import ( "errors" + "fmt" "github.com/OpenIMSDK/protocol/constant" "github.com/spf13/cobra" "google.golang.org/grpc" + "github.com/OpenIMSDK/tools/errs" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/tools/discoveryregistry" @@ -39,78 +42,78 @@ func NewRpcCmd(name string) *RpcCmd { } func (a *RpcCmd) Exec() error { - a.Command.Run = func(cmd *cobra.Command, args []string) { - a.port = a.getPortFlag(cmd) - a.prometheusPort = a.getPrometheusPortFlag(cmd) + a.Command.RunE = func(cmd *cobra.Command, args []string) error { + portFlag, err := a.getPortFlag(cmd) + if err != nil { + return err + } + a.port = portFlag + + prometheusPort, err := a.getPrometheusPortFlag(cmd) + if err != nil { + return err + } + a.prometheusPort = prometheusPort + + return nil } return a.Execute() } func (a *RpcCmd) StartSvr(name string, rpcFn func(discov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error { - if a.GetPortFlag() == 0 { - return errors.New("port is required") + portFlag, err := a.GetPortFlag() + if err != nil { + return err + } else { + a.port = portFlag } - return startrpc.Start(a.GetPortFlag(), name, a.GetPrometheusPortFlag(), rpcFn) + + return startrpc.Start(portFlag, name, a.GetPrometheusPortFlag(), rpcFn) } -func (a *RpcCmd) GetPortFromConfig(portType string) int { - switch a.Name { - case RpcPushServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImPushPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.PushPrometheusPort[0] - } - case RpcAuthServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImAuthPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.AuthPrometheusPort[0] - } - case RpcConversationServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImConversationPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.ConversationPrometheusPort[0] - } - case RpcFriendServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImFriendPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.FriendPrometheusPort[0] - } - case RpcGroupServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImGroupPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.GroupPrometheusPort[0] - } - case RpcMsgServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImMessagePort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.MessagePrometheusPort[0] - } - case RpcThirdServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImThirdPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.ThirdPrometheusPort[0] - } - case RpcUserServer: - if portType == constant.FlagPort { - return config2.Config.RpcPort.OpenImUserPort[0] - } - if portType == constant.FlagPrometheusPort { - return config2.Config.Prometheus.UserPrometheusPort[0] +func (a *RpcCmd) GetPortFromConfig(portType string) (int, error) { + portConfigMap := map[string]map[string]int{ + RpcPushServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImPushPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.PushPrometheusPort[0], + }, + RpcAuthServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImAuthPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.AuthPrometheusPort[0], + }, + RpcConversationServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImConversationPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.ConversationPrometheusPort[0], + }, + RpcFriendServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImFriendPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.FriendPrometheusPort[0], + }, + RpcGroupServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImGroupPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.GroupPrometheusPort[0], + }, + RpcMsgServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImMessagePort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.MessagePrometheusPort[0], + }, + RpcThirdServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImThirdPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.ThirdPrometheusPort[0], + }, + RpcUserServer: { + constant.FlagPort: config2.Config.RpcPort.OpenImUserPort[0], + constant.FlagPrometheusPort: config2.Config.Prometheus.UserPrometheusPort[0], + }, + } + + if portMap, ok := portConfigMap[a.Name]; ok { + if port, ok := portMap[portType]; ok { + return port, nil + } else { + return 0, errs.Wrap(errors.New("port type not found"), fmt.Sprintf("Failed to get port for %s", a.Name)) } } - return 0 + + return 0, errs.Wrap(fmt.Errorf("server name '%s' not found", a.Name), "Failed to get port configuration") } diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index 4037429e3..e216f6014 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -21,6 +21,7 @@ import ( "path/filepath" "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "gopkg.in/yaml.v3" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" @@ -36,32 +37,38 @@ const ( DefaultFolderPath = "../config/" ) -// return absolude path join ../config/, this is k8s container config path. -func GetDefaultConfigPath() string { +// GetDefaultConfigPath returns the absolute path to the default configuration directory +// relative to the executable's location. It is intended for use in Kubernetes container configurations. +// Errors are returned to the caller to allow for flexible error handling. +func GetDefaultConfigPath() (string, error) { executablePath, err := os.Executable() if err != nil { - fmt.Println("GetDefaultConfigPath error:", err.Error()) - return "" + return "", errs.Wrap(err, "failed to get executable path") } + // Calculate the config path as a directory relative to the executable's location configPath, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../config/")) if err != nil { - fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) - os.Exit(1) + return "", errs.Wrap(err, "failed to get output directory") } - return configPath + return configPath, nil } -// getProjectRoot returns the absolute path of the project root directory. -func GetProjectRoot() string { - executablePath, _ := os.Executable() +// GetProjectRoot returns the absolute path of the project root directory by navigating up from the directory +// containing the executable. It provides a detailed error if the path cannot be determined. +func GetProjectRoot() (string, error) { + executablePath, err := os.Executable() + if err != nil { + return "", errs.Wrap(err, "failed to retrieve executable path") + } + // Attempt to compute the project root by navigating up from the executable's directory projectRoot, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../../../../..")) if err != nil { - fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) - os.Exit(1) + return "", err } - return projectRoot + + return projectRoot, nil } func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options { @@ -83,42 +90,66 @@ func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options { return opts } +// initConfig loads configuration from a specified path into the provided config structure. +// If the specified config file does not exist, it attempts to load from the project's default "config" directory. +// It logs informative messages regarding the configuration path being used. func initConfig(config any, configName, configFolderPath string) error { - configFolderPath = filepath.Join(configFolderPath, configName) - _, err := os.Stat(configFolderPath) + configFilePath := filepath.Join(configFolderPath, configName) + _, err := os.Stat(configFilePath) if err != nil { if !os.IsNotExist(err) { - fmt.Println("stat config path error:", err.Error()) - return fmt.Errorf("stat config path error: %w", err) + return errs.Wrap(err, fmt.Sprintf("failed to check existence of config file at path: %s", configFilePath)) + } + var projectRoot string + projectRoot, err = GetProjectRoot() + if err != nil { + return err } - configFolderPath = filepath.Join(GetProjectRoot(), "config", configName) - fmt.Println("flag's path,enviment's path,default path all is not exist,using project path:", configFolderPath) + configFilePath = filepath.Join(projectRoot, "config", configName) + fmt.Printf("Configuration file not found at specified path. Falling back to project path: %s\n", configFilePath) } - data, err := os.ReadFile(configFolderPath) + + data, err := os.ReadFile(configFilePath) if err != nil { - return fmt.Errorf("read file error: %w", err) + // Wrap and return the error if reading the configuration file fails. + return errs.Wrap(err, fmt.Sprintf("failed to read configuration file at path: %s", configFilePath)) } + if err = yaml.Unmarshal(data, config); err != nil { - return fmt.Errorf("unmarshal yaml error: %w", err) + // Wrap and return the error if unmarshalling the YAML configuration fails. + return errs.Wrap(err, "failed to unmarshal YAML configuration") } - fmt.Println("The path of the configuration file to start the process:", configFolderPath) + fmt.Printf("Configuration file loaded successfully from path: %s\n", configFilePath) return nil } +// InitConfig initializes the application configuration by loading it from a specified folder path. +// If the folder path is not provided, it attempts to use the OPENIMCONFIG environment variable, +// and as a fallback, it uses the default configuration path. It loads both the main configuration +// and notification configuration, wrapping errors for better context. func InitConfig(configFolderPath string) error { + // Use the provided config folder path, or fallback to environment variable or default path if configFolderPath == "" { - envConfigPath := os.Getenv("OPENIMCONFIG") - if envConfigPath != "" { - configFolderPath = envConfigPath - } else { - configFolderPath = GetDefaultConfigPath() + configFolderPath = os.Getenv("OPENIMCONFIG") + if configFolderPath == "" { + var err error + configFolderPath, err = GetDefaultConfigPath() + if err != nil { + return err + } } } + // Initialize the main configuration if err := initConfig(&Config, FileName, configFolderPath); err != nil { return err } - return initConfig(&Config.Notification, NotificationFileName, configFolderPath) + // Initialize the notification configuration + if err := initConfig(&Config.Notification, NotificationFileName, configFolderPath); err != nil { + return err + } + + return nil } diff --git a/pkg/common/config/parse_test.go b/pkg/common/config/parse_test.go index 38171ec08..b980de7bd 100644 --- a/pkg/common/config/parse_test.go +++ b/pkg/common/config/parse_test.go @@ -31,7 +31,7 @@ func TestGetDefaultConfigPath(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := GetDefaultConfigPath(); got != tt.want { + if got, _ := GetDefaultConfigPath(); got != tt.want { t.Errorf("GetDefaultConfigPath() = %v, want %v", got, tt.want) } }) @@ -47,7 +47,7 @@ func TestGetProjectRoot(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := GetProjectRoot(); got != tt.want { + if got, _ := GetProjectRoot(); got != tt.want { t.Errorf("GetProjectRoot() = %v, want %v", got, tt.want) } }) diff --git a/pkg/common/convert/black.go b/pkg/common/convert/black.go index 50c270dcb..57140f436 100644 --- a/pkg/common/convert/black.go +++ b/pkg/common/convert/black.go @@ -23,11 +23,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) -func BlackDB2Pb( - ctx context.Context, - blackDBs []*relation.BlackModel, - f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), -) (blackPbs []*sdk.BlackInfo, err error) { +func BlackDB2Pb(ctx context.Context, blackDBs []*relation.BlackModel, f func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (blackPbs []*sdk.BlackInfo, err error) { if len(blackDBs) == 0 { return nil, nil } diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index f3a19e459..0e9a05766 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -53,11 +53,7 @@ func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, }, nil } -func FriendsDB2Pb( - ctx context.Context, - friendsDB []*relation.FriendModel, - getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), -) (friendsPb []*sdkws.FriendInfo, err error) { +func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (friendsPb []*sdkws.FriendInfo, err error) { if len(friendsDB) == 0 { return nil, nil } @@ -89,8 +85,7 @@ func FriendsDB2Pb( } -func FriendRequestDB2Pb( - ctx context.Context, +func FriendRequestDB2Pb(ctx context.Context, friendRequests []*relation.FriendRequestModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), ) ([]*sdkws.FriendRequest, error) { diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index d1abe945c..48bc3c285 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -46,11 +46,7 @@ type BlackCacheRedis struct { blackDB relationtb.BlackModelInterface } -func NewBlackCacheRedis( - rdb redis.UniversalClient, - blackDB relationtb.BlackModelInterface, - options rockscache.Options, -) BlackCache { +func NewBlackCacheRedis(rdb redis.UniversalClient, blackDB relationtb.BlackModelInterface, options rockscache.Options) BlackCache { rcClient := rockscache.NewClient(rdb, options) return &BlackCacheRedis{ diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index 3cec73be5..a41a4f460 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -49,7 +49,7 @@ func NewRedis() (redis.UniversalClient, error) { overrideConfigFromEnv() if len(config.Config.Redis.Address) == 0 { - return nil, errs.Wrap(errors.New("redis address is empty")) + return nil, errs.Wrap(errors.New("redis address is empty"), "Redis configuration error") } specialerror.AddReplace(redis.Nil, errs.ErrRecordNotFound) var rdb redis.UniversalClient @@ -65,9 +65,9 @@ func NewRedis() (redis.UniversalClient, error) { rdb = redis.NewClient(&redis.Options{ Addr: config.Config.Redis.Address[0], Username: config.Config.Redis.Username, - Password: config.Config.Redis.Password, - DB: 0, // use default DB - PoolSize: 100, // connection pool size + Password: config.Config.Redis.Password, // no password set + DB: 0, // use default DB + PoolSize: 100, // connection pool size MaxRetries: maxRetry, }) } @@ -77,9 +77,9 @@ func NewRedis() (redis.UniversalClient, error) { defer cancel() err = rdb.Ping(ctx).Err() if err != nil { - uriFormat := "address:%s, username:%s, password:%s, clusterMode:%t, enablePipeline:%t" - errMsg := fmt.Sprintf(uriFormat, config.Config.Redis.Address, config.Config.Redis.Username, config.Config.Redis.Password, config.Config.Redis.ClusterMode, config.Config.Redis.EnablePipeline) - return nil, errs.Wrap(err, errMsg) + uriFormat := "address:%v, username:%s, clusterMode:%t, enablePipeline:%t" + errMsg := fmt.Sprintf(uriFormat, config.Config.Redis.Address, config.Config.Redis.Username, config.Config.Redis.ClusterMode, config.Config.Redis.EnablePipeline) + return nil, errs.Wrap(err, "Redis connection failed: %s", errMsg) } redisClient = rdb return rdb, err @@ -98,9 +98,11 @@ func overrideConfigFromEnv() { config.Config.Redis.Address = strings.Split(envAddr, ",") } } + if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" { config.Config.Redis.Username = envUser } + if envPass := os.Getenv("REDIS_PASSWORD"); envPass != "" { config.Config.Redis.Password = envPass } diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 7eb486c9a..fee982dc5 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -134,7 +134,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin } bs, err := json.Marshal(t) if err != nil { - return "", errs.Wrap(err) + return "", errs.Wrap(err, "marshal failed") } write = true @@ -152,8 +152,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin err = json.Unmarshal([]byte(v), &t) if err != nil { log.ZError(ctx, "cache json.Unmarshal failed", err, "key", key, "value", v, "expire", expire) - - return t, errs.Wrap(err) + return t, errs.Wrap(err, "unmarshal failed") } return t, nil @@ -197,14 +196,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin // return tArrays, nil //} -func batchGetCache2[T any, K comparable]( - ctx context.Context, - rcClient *rockscache.Client, - expire time.Duration, - keys []K, - keyFn func(key K) string, - fns func(ctx context.Context, key K) (T, error), -) ([]T, error) { +func batchGetCache2[T any, K comparable](ctx context.Context, rcClient *rockscache.Client, expire time.Duration, keys []K, keyFn func(key K) string, fns func(ctx context.Context, key K) (T, error)) ([]T, error) { if len(keys) == 0 { return nil, nil } diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 8a54e1a8b..5cef509ed 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -24,12 +24,11 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/OpenIMSDK/tools/errs" - "github.com/gogo/protobuf/jsonpb" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" @@ -433,7 +432,7 @@ func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID str for _, msg := range msgs { s, err := msgprocessor.Pb2String(msg) if err != nil { - return 0, errs.Wrap(err, "pb.marshal") + return 0, err } key := c.getMessageCacheKey(conversationID, msg.Seq) @@ -442,7 +441,7 @@ func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID str results, err := pipe.Exec(ctx) if err != nil { - return 0, errs.Wrap(err, "pipe.set") + return 0, errs.Wrap(err) } for _, res := range results { diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 979bd06e4..f444a2c46 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -66,11 +66,7 @@ type UserCacheRedis struct { rcClient *rockscache.Client } -func NewUserCacheRedis( - rdb redis.UniversalClient, - userDB relationtb.UserModelInterface, - options rockscache.Options, -) UserCache { +func NewUserCacheRedis(rdb redis.UniversalClient, userDB relationtb.UserModelInterface, options rockscache.Options) UserCache { rcClient := rockscache.NewClient(rdb, options) return &UserCacheRedis{ @@ -282,8 +278,8 @@ func (u *UserCacheRedis) refreshStatusOnline(ctx context.Context, userID string, var onlineStatus user.OnlineStatus if !isNil { err2 := json.Unmarshal([]byte(result), &onlineStatus) - if err != nil { - return errs.Wrap(err2) + if err2 != nil { + return errs.Wrap(err, "json.Unmarshal failed") } onlineStatus.PlatformIDs = RemoveRepeatedElementsInList(append(onlineStatus.PlatformIDs, platformID)) } else { @@ -293,7 +289,7 @@ func (u *UserCacheRedis) refreshStatusOnline(ctx context.Context, userID string, onlineStatus.UserID = userID newjsonData, err := json.Marshal(&onlineStatus) if err != nil { - return errs.Wrap(err) + return errs.Wrap(err, "json.Marshal failed") } _, err = u.rdb.HSet(ctx, key, userID, string(newjsonData)).Result() if err != nil { diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 18c64ad8f..13bc066ca 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -30,9 +30,9 @@ import ( ) type AuthDatabase interface { - // 结果为空 不返回错误 + // If the result is empty, no error is returned. GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) - // 创建token + // Create token CreateToken(ctx context.Context, userID string, platformID int) (string, error) } @@ -47,16 +47,12 @@ func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire} } -// 结果为空 不返回错误. -func (a *authDatabase) GetTokensWithoutError( - ctx context.Context, - userID string, - platformID int, -) (map[string]int, error) { +// If the result is empty. +func (a *authDatabase) GetTokensWithoutError(ctx context.Context, userID string, platformID int) (map[string]int, error) { return a.cache.GetTokensWithoutError(ctx, userID, platformID) } -// 创建token. +// Create Token. func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformID int) (string, error) { tokens, err := a.cache.GetTokensWithoutError(ctx, userID, platformID) if err != nil { @@ -80,7 +76,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) tokenString, err := token.SignedString([]byte(a.accessSecret)) if err != nil { - return "", errs.Wrap(err) + return "", errs.Wrap(err, "token.SignedString") } return tokenString, a.cache.AddTokenFlag(ctx, userID, platformID, tokenString, constant.NormalToken) } diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go index e68d06b01..3c8a47a01 100644 --- a/pkg/common/db/controller/black.go +++ b/pkg/common/db/controller/black.go @@ -27,14 +27,14 @@ import ( ) type BlackDatabase interface { - // Create 增加黑名单 + // Create add BlackList Create(ctx context.Context, blacks []*relation.BlackModel) (err error) - // Delete 删除黑名单 + // Delete delete BlackList Delete(ctx context.Context, blacks []*relation.BlackModel) (err error) - // FindOwnerBlacks 获取黑名单列表 + // FindOwnerBlacks get BlackList list FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*relation.BlackModel, err error) FindBlackInfos(ctx context.Context, ownerUserID string, userIDs []string) (blacks []*relation.BlackModel, err error) - // CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true) + // CheckIn Check whether user2 is in the black list of user1 (inUser1Blacks==true) Check whether user1 is in the black list of user2 (inUser2Blacks==true) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) } @@ -47,7 +47,7 @@ func NewBlackDatabase(black relation.BlackModelInterface, cache cache.BlackCache return &blackDatabase{black, cache} } -// Create 增加黑名单. +// Create Add Blacklist. func (b *blackDatabase) Create(ctx context.Context, blacks []*relation.BlackModel) (err error) { if err := b.black.Create(ctx, blacks); err != nil { return err @@ -55,7 +55,7 @@ func (b *blackDatabase) Create(ctx context.Context, blacks []*relation.BlackMode return b.deleteBlackIDsCache(ctx, blacks) } -// Delete 删除黑名单. +// Delete Delete Blacklist. func (b *blackDatabase) Delete(ctx context.Context, blacks []*relation.BlackModel) (err error) { if err := b.black.Delete(ctx, blacks); err != nil { return err @@ -63,6 +63,7 @@ func (b *blackDatabase) Delete(ctx context.Context, blacks []*relation.BlackMode return b.deleteBlackIDsCache(ctx, blacks) } +// FindOwnerBlacks Get Blacklist List. func (b *blackDatabase) deleteBlackIDsCache(ctx context.Context, blacks []*relation.BlackModel) (err error) { cache := b.cache.NewCache() for _, black := range blacks { @@ -71,16 +72,13 @@ func (b *blackDatabase) deleteBlackIDsCache(ctx context.Context, blacks []*relat return cache.ExecDel(ctx) } -// FindOwnerBlacks 获取黑名单列表. +// FindOwnerBlacks Get Blacklist List. func (b *blackDatabase) FindOwnerBlacks(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, blacks []*relation.BlackModel, err error) { return b.black.FindOwnerBlacks(ctx, ownerUserID, pagination) } -// CheckIn 检查user2是否在user1的黑名单列表中(inUser1Blacks==true) 检查user1是否在user2的黑名单列表中(inUser2Blacks==true). -func (b *blackDatabase) CheckIn( - ctx context.Context, - userID1, userID2 string, -) (inUser1Blacks bool, inUser2Blacks bool, err error) { +// FindOwnerBlacks Get Blacklist List. +func (b *blackDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Blacks bool, inUser2Blacks bool, err error) { userID1BlackIDs, err := b.cache.GetBlackIDs(ctx, userID1) if err != nil { return @@ -93,10 +91,12 @@ func (b *blackDatabase) CheckIn( return utils.IsContain(userID2, userID1BlackIDs), utils.IsContain(userID1, userID2BlackIDs), nil } +// FindBlackIDs Get Blacklist List. func (b *blackDatabase) FindBlackIDs(ctx context.Context, ownerUserID string) (blackIDs []string, err error) { return b.cache.GetBlackIDs(ctx, ownerUserID) } +// FindBlackInfos Get Blacklist List. func (b *blackDatabase) FindBlackInfos(ctx context.Context, ownerUserID string, userIDs []string) (blacks []*relation.BlackModel, err error) { return b.black.FindOwnerBlackInfos(ctx, ownerUserID, userIDs) } diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index c53d4ab87..0c64169a7 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -32,32 +32,40 @@ import ( ) type ConversationDatabase interface { - // UpdateUserConversationFiled 更新用户该会话的属性信息 - UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error - // CreateConversation 创建一批新的会话 + // UpdateUsersConversationField updates the properties of a conversation for specified users. + UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error + // CreateConversation creates a batch of new conversations. CreateConversation(ctx context.Context, conversations []*relationtb.ConversationModel) error - // SyncPeerUserPrivateConversation 同步对端私聊会话内部保证事务操作 + // SyncPeerUserPrivateConversationTx ensures transactional operation while syncing private conversations between peers. SyncPeerUserPrivateConversationTx(ctx context.Context, conversation []*relationtb.ConversationModel) error - // FindConversations 根据会话ID获取某个用户的多个会话 + // FindConversations retrieves multiple conversations of a user by conversation IDs. FindConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*relationtb.ConversationModel, error) - // FindRecvMsgNotNotifyUserIDs 获取超级大群开启免打扰的用户ID - //FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) - // GetUserAllConversation 获取一个用户在服务器上所有的会话 + // GetUserAllConversation fetches all conversations of a user on the server. GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationtb.ConversationModel, error) - // SetUserConversations 设置用户多个会话属性,如果会话不存在则创建,否则更新,内部保证原子性 + // SetUserConversations sets multiple conversation properties for a user, creates new conversations if they do not exist, or updates them otherwise. This operation is atomic. SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationtb.ConversationModel) error - // SetUsersConversationFiledTx 设置多个用户会话关于某个字段的更新操作,如果会话不存在则创建,否则更新,内部保证事务操作 - SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, filedMap map[string]any) error + // SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is transactional. + SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, fieldMap map[string]any) error + // CreateGroupChatConversation creates a group chat conversation for the specified group ID and user IDs. CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error + // GetConversationIDs retrieves conversation IDs for a given user. GetConversationIDs(ctx context.Context, userID string) ([]string, error) + // GetUserConversationIDsHash gets the hash of conversation IDs for a given user. GetUserConversationIDsHash(ctx context.Context, ownerUserID string) (hash uint64, err error) + // GetAllConversationIDs fetches all conversation IDs. GetAllConversationIDs(ctx context.Context) ([]string, error) + // GetAllConversationIDsNumber returns the number of all conversation IDs. GetAllConversationIDsNumber(ctx context.Context) (int64, error) + // PageConversationIDs paginates through conversation IDs based on the specified pagination settings. PageConversationIDs(ctx context.Context, pagination pagination.Pagination) (conversationIDs []string, err error) - //GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) + // GetConversationsByConversationID retrieves conversations by their IDs. GetConversationsByConversationID(ctx context.Context, conversationIDs []string) ([]*relationtb.ConversationModel, error) + // GetConversationIDsNeedDestruct fetches conversations that need to be destructed based on specific criteria. GetConversationIDsNeedDestruct(ctx context.Context) ([]*relationtb.ConversationModel, error) + // GetConversationNotReceiveMessageUserIDs gets user IDs for users in a conversation who have not received messages. GetConversationNotReceiveMessageUserIDs(ctx context.Context, conversationID string) ([]string, error) + //GetUserAllHasReadSeqs(ctx context.Context, ownerUserID string) (map[string]int64, error) + //FindRecvMsgNotNotifyUserIDs(ctx context.Context, groupID string) ([]string, error) } func NewConversationDatabase(conversation relationtb.ConversationModelInterface, cache cache.ConversationCache, tx tx.CtxTx) ConversationDatabase { @@ -74,7 +82,7 @@ type conversationDatabase struct { tx tx.CtxTx } -func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, filedMap map[string]any) (err error) { +func (c *conversationDatabase) SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, fieldMap map[string]any) (err error) { return c.tx.Transaction(ctx, func(ctx context.Context) error { cache := c.cache.NewCache() if conversation.GroupID != "" { @@ -85,22 +93,22 @@ func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, return err } if len(haveUserIDs) > 0 { - _, err = c.conversationDB.UpdateByMap(ctx, haveUserIDs, conversation.ConversationID, filedMap) + _, err = c.conversationDB.UpdateByMap(ctx, haveUserIDs, conversation.ConversationID, fieldMap) if err != nil { return err } cache = cache.DelUsersConversation(conversation.ConversationID, haveUserIDs...) - if _, ok := filedMap["has_read_seq"]; ok { + if _, ok := fieldMap["has_read_seq"]; ok { for _, userID := range haveUserIDs { cache = cache.DelUserAllHasReadSeqs(userID, conversation.ConversationID) } } - if _, ok := filedMap["recv_msg_opt"]; ok { + if _, ok := fieldMap["recv_msg_opt"]; ok { cache = cache.DelConversationNotReceiveMessageUserIDs(conversation.ConversationID) } } NotUserIDs := utils.DifferenceString(haveUserIDs, userIDs) - log.ZDebug(ctx, "SetUsersConversationFiledTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs) + log.ZDebug(ctx, "SetUsersConversationFieldTx", "NotUserIDs", NotUserIDs, "haveUserIDs", haveUserIDs, "userIDs", userIDs) var conversations []*relationtb.ConversationModel now := time.Now() for _, v := range NotUserIDs { @@ -123,7 +131,7 @@ func (c *conversationDatabase) SetUsersConversationFiledTx(ctx context.Context, }) } -func (c *conversationDatabase) UpdateUsersConversationFiled(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error { +func (c *conversationDatabase) UpdateUsersConversationField(ctx context.Context, userIDs []string, conversationID string, args map[string]any) error { _, err := c.conversationDB.UpdateByMap(ctx, userIDs, conversationID, args) if err != nil { return err diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 3b98f5d7b..bb5ec3087 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -16,6 +16,7 @@ package controller import ( "context" + "fmt" "time" "github.com/OpenIMSDK/tools/pagination" @@ -89,20 +90,30 @@ func NewFriendDatabase(friend relation.FriendModelInterface, friendRequest relat return &friendDatabase{friend: friend, friendRequest: friendRequest, cache: cache, tx: tx} } -// ok 检查user2是否在user1的好友列表中(inUser1Friends==true) 检查user1是否在user2的好友列表中(inUser2Friends==true). +// CheckIn verifies if user2 is in user1's friend list (inUser1Friends returns true) and +// if user1 is in user2's friend list (inUser2Friends returns true). func (f *friendDatabase) CheckIn(ctx context.Context, userID1, userID2 string) (inUser1Friends bool, inUser2Friends bool, err error) { + // Retrieve friend IDs of userID1 from the cache userID1FriendIDs, err := f.cache.GetFriendIDs(ctx, userID1) if err != nil { + err = fmt.Errorf("error retrieving friend IDs for user %s: %w", userID1, err) return } + + // Retrieve friend IDs of userID2 from the cache userID2FriendIDs, err := f.cache.GetFriendIDs(ctx, userID2) if err != nil { + err = fmt.Errorf("error retrieving friend IDs for user %s: %w", userID2, err) return } - return utils.IsContain(userID2, userID1FriendIDs), utils.IsContain(userID1, userID2FriendIDs), nil + + // Check if userID2 is in userID1's friend list and vice versa + inUser1Friends = utils.IsContain(userID2, userID1FriendIDs) + inUser2Friends = utils.IsContain(userID1, userID2FriendIDs) + return inUser1Friends, inUser2Friends, nil } -// 增加或者更新好友申请 如果之前有记录则更新,没有记录则新增. +// AddFriendRequest adds or updates a friend request. func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUserID string, reqMsg string, ex string) (err error) { return f.tx.Transaction(ctx, func(ctx context.Context) error { _, err := f.friendRequest.Take(ctx, fromUserID, toUserID) @@ -126,11 +137,11 @@ func (f *friendDatabase) AddFriendRequest(ctx context.Context, fromUserID, toUse }) } -// (1)先判断是否在好友表 (在不在都不返回错误) (2)对于不在好友列表的 插入即可. +// (1) First determine whether it is in the friends list (in or out does not return an error) (2) for not in the friends list can be inserted. func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, friendUserIDs []string, addSource int32) (err error) { return f.tx.Transaction(ctx, func(ctx context.Context) error { cache := f.cache.NewCache() - // 先find 找出重复的 去掉重复的 + // User find friends fs1, err := f.friend.FindFriends(ctx, ownerUserID, friendUserIDs) if err != nil { return err @@ -170,26 +181,37 @@ func (f *friendDatabase) BecomeFriends(ctx context.Context, ownerUserID string, }) } -// 拒绝好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)修改申请记录 已拒绝. -func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) { +// RefuseFriendRequest rejects a friend request. It first checks for an existing, unprocessed request. +// If no such request exists, it returns an error. Otherwise, it marks the request as refused. +func (f *friendDatabase) RefuseFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) error { + // Attempt to retrieve the friend request from the database. fr, err := f.friendRequest.Take(ctx, friendRequest.FromUserID, friendRequest.ToUserID) if err != nil { - return err + return fmt.Errorf("failed to retrieve friend request from %s to %s: %w", friendRequest.FromUserID, friendRequest.ToUserID, err) } + + // Check if the friend request has already been handled. if fr.HandleResult != 0 { - return errs.ErrArgs.Wrap("the friend request has been processed") + return fmt.Errorf("friend request from %s to %s has already been processed", friendRequest.FromUserID, friendRequest.ToUserID) } - log.ZDebug(ctx, "refuse friend request", "friendRequest db", fr, "friendRequest arg", friendRequest) + + // Log the action of refusing the friend request for debugging and auditing purposes. + log.ZDebug(ctx, "Refusing friend request", map[string]interface{}{ + "DB_FriendRequest": fr, + "Arg_FriendRequest": friendRequest, + }) + + // Mark the friend request as refused and update the handle time. friendRequest.HandleResult = constant.FriendResponseRefuse friendRequest.HandleTime = time.Now() - err = f.friendRequest.Update(ctx, friendRequest) - if err != nil { - return err + if err := f.friendRequest.Update(ctx, friendRequest); err != nil { + return fmt.Errorf("failed to update friend request from %s to %s as refused: %w", friendRequest.FromUserID, friendRequest.ToUserID, err) } + return nil } -// AgreeFriendRequest 同意好友申请 (1)检查是否有申请记录且为未处理状态 (没有记录返回错误) (2)检查是否好友(不返回错误) (3) 建立双向好友关系(存在的忽略). +// AgreeFriendRequest accepts a friend request. It first checks for an existing, unprocessed request. func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest *relation.FriendRequestModel) (err error) { return f.tx.Transaction(ctx, func(ctx context.Context) error { defer log.ZDebug(ctx, "return line") @@ -227,10 +249,10 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest * return err } existsMap := utils.SliceSet(utils.Slice(exists, func(friend *relation.FriendModel) [2]string { - return [...]string{friend.OwnerUserID, friend.FriendUserID} // 自己 - 好友 + return [...]string{friend.OwnerUserID, friend.FriendUserID} // My - Friend })) var adds []*relation.FriendModel - if _, ok := existsMap[[...]string{friendRequest.ToUserID, friendRequest.FromUserID}]; !ok { // 自己 - 好友 + if _, ok := existsMap[[...]string{friendRequest.ToUserID, friendRequest.FromUserID}]; !ok { // My - Friend adds = append( adds, &relation.FriendModel{ @@ -241,7 +263,7 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest * }, ) } - if _, ok := existsMap[[...]string{friendRequest.FromUserID, friendRequest.ToUserID}]; !ok { // 好友 - 自己 + if _, ok := existsMap[[...]string{friendRequest.FromUserID, friendRequest.ToUserID}]; !ok { // My - Friend adds = append( adds, &relation.FriendModel{ @@ -261,7 +283,7 @@ func (f *friendDatabase) AgreeFriendRequest(ctx context.Context, friendRequest * }) } -// 删除好友 外部判断是否好友关系. +// Delete removes a friend relationship. It is assumed that the external caller has verified the friendship status. func (f *friendDatabase) Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) { if err := f.friend.Delete(ctx, ownerUserID, friendUserIDs); err != nil { return err @@ -269,7 +291,7 @@ func (f *friendDatabase) Delete(ctx context.Context, ownerUserID string, friendU return f.cache.DelFriendIDs(append(friendUserIDs, ownerUserID)...).ExecDel(ctx) } -// 更新好友备注 零值也支持. +// UpdateRemark updates the remark for a friend. Zero value for remark is also supported. func (f *friendDatabase) UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) { if err := f.friend.UpdateRemark(ctx, ownerUserID, friendUserID, remark); err != nil { return err @@ -277,27 +299,27 @@ func (f *friendDatabase) UpdateRemark(ctx context.Context, ownerUserID, friendUs return f.cache.DelFriend(ownerUserID, friendUserID).ExecDel(ctx) } -// 获取ownerUserID的好友列表 无结果不返回错误. +// PageOwnerFriends retrieves the list of friends for the ownerUserID. It does not return an error if the result is empty. func (f *friendDatabase) PageOwnerFriends(ctx context.Context, ownerUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error) { return f.friend.FindOwnerFriends(ctx, ownerUserID, pagination) } -// friendUserID在哪些人的好友列表中. +// PageInWhoseFriends identifies in whose friend lists the friendUserID appears. func (f *friendDatabase) PageInWhoseFriends(ctx context.Context, friendUserID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendModel, err error) { return f.friend.FindInWhoseFriends(ctx, friendUserID, pagination) } -// 获取我发出去的好友申请 无结果不返回错误. +// PageFriendRequestFromMe retrieves friend requests sent by me. It does not return an error if the result is empty. func (f *friendDatabase) PageFriendRequestFromMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error) { return f.friendRequest.FindFromUserID(ctx, userID, pagination) } -// 获取我收到的的好友申请 无结果不返回错误. +// PageFriendRequestToMe retrieves friend requests received by me. It does not return an error if the result is empty. func (f *friendDatabase) PageFriendRequestToMe(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, friends []*relation.FriendRequestModel, err error) { return f.friendRequest.FindToUserID(ctx, userID, pagination) } -// 获取某人指定好友的信息 如果有好友不存在,也返回错误. +// FindFriendsWithError retrieves specified friends' information for ownerUserID. Returns an error if any friend does not exist. func (f *friendDatabase) FindFriendsWithError(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*relation.FriendModel, err error) { friends, err = f.friend.FindFriends(ctx, ownerUserID, friendUserIDs) if err != nil { diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index decd868d6..6f208cc24 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -31,47 +31,79 @@ import ( ) type GroupDatabase interface { - // Group + // CreateGroup creates new groups along with their members. CreateGroup(ctx context.Context, groups []*relationtb.GroupModel, groupMembers []*relationtb.GroupMemberModel) error + // TakeGroup retrieves a single group by its ID. TakeGroup(ctx context.Context, groupID string) (group *relationtb.GroupModel, err error) + // FindGroup retrieves multiple groups by their IDs. FindGroup(ctx context.Context, groupIDs []string) (groups []*relationtb.GroupModel, err error) + // SearchGroup searches for groups based on a keyword and pagination settings, returns total count and groups. SearchGroup(ctx context.Context, keyword string, pagination pagination.Pagination) (int64, []*relationtb.GroupModel, error) + // UpdateGroup updates the properties of a group identified by its ID. UpdateGroup(ctx context.Context, groupID string, data map[string]any) error - DismissGroup(ctx context.Context, groupID string, deleteMember bool) error // 解散群,并删除群成员 + // DismissGroup disbands a group and optionally removes its members based on the deleteMember flag. + DismissGroup(ctx context.Context, groupID string, deleteMember bool) error + // TakeGroupMember retrieves a specific group member by group ID and user ID. TakeGroupMember(ctx context.Context, groupID string, userID string) (groupMember *relationtb.GroupMemberModel, err error) + // TakeGroupOwner retrieves the owner of a group by group ID. TakeGroupOwner(ctx context.Context, groupID string) (*relationtb.GroupMemberModel, error) - FindGroupMembers(ctx context.Context, groupID string, userIDs []string) (groupMembers []*relationtb.GroupMemberModel, err error) // * - FindGroupMemberUser(ctx context.Context, groupIDs []string, userID string) (groupMembers []*relationtb.GroupMemberModel, err error) // * - FindGroupMemberRoleLevels(ctx context.Context, groupID string, roleLevels []int32) (groupMembers []*relationtb.GroupMemberModel, err error) // * - FindGroupMemberAll(ctx context.Context, groupID string) (groupMembers []*relationtb.GroupMemberModel, err error) // * + // FindGroupMembers retrieves members of a group filtered by user IDs. + FindGroupMembers(ctx context.Context, groupID string, userIDs []string) (groupMembers []*relationtb.GroupMemberModel, err error) + // FindGroupMemberUser retrieves groups that a user is a member of, filtered by group IDs. + FindGroupMemberUser(ctx context.Context, groupIDs []string, userID string) (groupMembers []*relationtb.GroupMemberModel, err error) + // FindGroupMemberRoleLevels retrieves group members filtered by their role levels within a group. + FindGroupMemberRoleLevels(ctx context.Context, groupID string, roleLevels []int32) (groupMembers []*relationtb.GroupMemberModel, err error) + // FindGroupMemberAll retrieves all members of a group. + FindGroupMemberAll(ctx context.Context, groupID string) (groupMembers []*relationtb.GroupMemberModel, err error) + // FindGroupsOwner retrieves the owners for multiple groups. FindGroupsOwner(ctx context.Context, groupIDs []string) ([]*relationtb.GroupMemberModel, error) + // FindGroupMemberUserID retrieves the user IDs of all members in a group. FindGroupMemberUserID(ctx context.Context, groupID string) ([]string, error) + // FindGroupMemberNum retrieves the number of members in a group. FindGroupMemberNum(ctx context.Context, groupID string) (uint32, error) + // FindUserManagedGroupID retrieves group IDs managed by a user. FindUserManagedGroupID(ctx context.Context, userID string) (groupIDs []string, err error) + // PageGroupRequest paginates through group requests for specified groups. PageGroupRequest(ctx context.Context, groupIDs []string, pagination pagination.Pagination) (int64, []*relationtb.GroupRequestModel, error) + // GetGroupRoleLevelMemberIDs retrieves user IDs of group members with a specific role level. GetGroupRoleLevelMemberIDs(ctx context.Context, groupID string, roleLevel int32) ([]string, error) + // PageGetJoinGroup paginates through groups that a user has joined. PageGetJoinGroup(ctx context.Context, userID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*relationtb.GroupMemberModel, err error) + // PageGetGroupMember paginates through members of a group. PageGetGroupMember(ctx context.Context, groupID string, pagination pagination.Pagination) (total int64, totalGroupMembers []*relationtb.GroupMemberModel, err error) + // SearchGroupMember searches for group members based on a keyword, group ID, and pagination settings. SearchGroupMember(ctx context.Context, keyword string, groupID string, pagination pagination.Pagination) (int64, []*relationtb.GroupMemberModel, error) + // HandlerGroupRequest processes a group join request with a specified result. HandlerGroupRequest(ctx context.Context, groupID string, userID string, handledMsg string, handleResult int32, member *relationtb.GroupMemberModel) error + // DeleteGroupMember removes specified users from a group. DeleteGroupMember(ctx context.Context, groupID string, userIDs []string) error + // MapGroupMemberUserID maps group IDs to their members' simplified user IDs. MapGroupMemberUserID(ctx context.Context, groupIDs []string) (map[string]*relationtb.GroupSimpleUserID, error) + // MapGroupMemberNum maps group IDs to their member count. MapGroupMemberNum(ctx context.Context, groupIDs []string) (map[string]uint32, error) - TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error // 转让群 + // TransferGroupOwner transfers the ownership of a group to another user. + TransferGroupOwner(ctx context.Context, groupID string, oldOwnerUserID, newOwnerUserID string, roleLevel int32) error + // UpdateGroupMember updates properties of a group member. UpdateGroupMember(ctx context.Context, groupID string, userID string, data map[string]any) error + // UpdateGroupMembers batch updates properties of group members. UpdateGroupMembers(ctx context.Context, data []*relationtb.BatchUpdateGroupMember) error - // GroupRequest + + // CreateGroupRequest creates new group join requests. CreateGroupRequest(ctx context.Context, requests []*relationtb.GroupRequestModel) error + // TakeGroupRequest retrieves a specific group join request. TakeGroupRequest(ctx context.Context, groupID string, userID string) (*relationtb.GroupRequestModel, error) + // FindGroupRequests retrieves multiple group join requests. FindGroupRequests(ctx context.Context, groupID string, userIDs []string) ([]*relationtb.GroupRequestModel, error) + // PageGroupRequestUser paginates through group join requests made by a user. PageGroupRequestUser(ctx context.Context, userID string, pagination pagination.Pagination) (int64, []*relationtb.GroupRequestModel, error) - // 获取群总数 + // CountTotal counts the total number of groups as of a certain date. CountTotal(ctx context.Context, before *time.Time) (count int64, err error) - // 获取范围内群增量 + // CountRangeEverydayTotal counts the daily group creation total within a specified date range. CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) + // DeleteGroupMemberHash deletes the hash entries for group members in specified groups. DeleteGroupMemberHash(ctx context.Context, groupIDs []string) error } diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index 7eac624a7..add9b63ce 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -48,33 +48,32 @@ const ( updateKeyRevoke ) +// CommonMsgDatabase defines the interface for message database operations. type CommonMsgDatabase interface { - // 批量插入消息 + // BatchInsertChat2DB inserts a batch of messages into the database for a specific conversation. BatchInsertChat2DB(ctx context.Context, conversationID string, msgs []*sdkws.MsgData, currentMaxSeq int64) error - // 撤回消息 + // RevokeMsg revokes a message in a conversation. RevokeMsg(ctx context.Context, conversationID string, seq int64, revoke *unrelationtb.RevokeModel) error - // mark as read + // MarkSingleChatMsgsAsRead marks messages as read for a single chat by sequence numbers. MarkSingleChatMsgsAsRead(ctx context.Context, userID string, conversationID string, seqs []int64) error - // 刪除redis中消息缓存 + // DeleteMessagesFromCache deletes message caches from Redis by sequence numbers. DeleteMessagesFromCache(ctx context.Context, conversationID string, seqs []int64) error + // DelUserDeleteMsgsList deletes user's message deletion list. DelUserDeleteMsgsList(ctx context.Context, conversationID string, seqs []int64) - // incrSeq然后批量插入缓存 + // BatchInsertChat2Cache increments the sequence number and then batch inserts messages into the cache. BatchInsertChat2Cache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (seq int64, isNewConversation bool, err error) - - // 通过seqList获取mongo中写扩散消息 + // GetMsgBySeqsRange retrieves messages from MongoDB by a range of sequence numbers. GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) - // 通过seqList获取大群在 mongo里面的消息 + // GetMsgBySeqs retrieves messages for large groups from MongoDB by sequence numbers. GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) - // 删除会话消息重置最小seq, remainTime为消息保留的时间单位秒,超时消息删除, 传0删除所有消息(此方法不删除redis cache) + // DeleteConversationMsgsAndSetMinSeq deletes conversation messages and resets the minimum sequence number. If `remainTime` is 0, all messages are deleted (this method does not delete Redis cache). DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error - // 用户标记删除过期消息返回标记删除的seq列表 + // UserMsgsDestruct marks messages for deletion based on destruct time and returns a list of sequence numbers for marked messages. UserMsgsDestruct(ctx context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) - - // 用户根据seq删除消息 + // DeleteUserMsgsBySeqs allows a user to delete messages based on sequence numbers. DeleteUserMsgsBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) error - // 物理删除消息置空 + // DeleteMsgsPhysicalBySeqs physically deletes messages by emptying them based on sequence numbers. DeleteMsgsPhysicalBySeqs(ctx context.Context, conversationID string, seqs []int64) error - SetMaxSeq(ctx context.Context, conversationID string, maxSeq int64) error GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) GetMaxSeq(ctx context.Context, conversationID string) (int64, error) @@ -200,7 +199,7 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI } num := db.msg.GetSingleGocMsgNum() // num = 100 - for i, field := range fields { // 检查类型 + for i, field := range fields { // Check the type of the field var ok bool switch key { case updateKeyMsg: @@ -218,7 +217,7 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI return errs.ErrInternalServer.Wrap("field type is invalid") } } - // 返回值为true表示数据库存在该文档,false表示数据库不存在该文档 + // Returns true if the document exists in the database, false if the document does not exist in the database updateMsgModel := func(seq int64, i int) (bool, error) { var ( res *mongo.UpdateResult @@ -240,21 +239,21 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI } tryUpdate := true for i := 0; i < len(fields); i++ { - seq := firstSeq + int64(i) // 当前seq + seq := firstSeq + int64(i) // Current sequence number if tryUpdate { matched, err := updateMsgModel(seq, i) if err != nil { return err } if matched { - continue // 匹配到了,继续下一个(不一定修改) + continue // The current data has been updated, skip the current data } } doc := unrelationtb.MsgDocModel{ DocID: db.msg.GetDocID(conversationID, seq), Msg: make([]*unrelationtb.MsgInfoModel, num), } - var insert int // 插入的数量 + var insert int // Inserted data number for j := i; j < len(fields); j++ { seq = firstSeq + int64(j) if db.msg.GetDocID(conversationID, seq) != doc.DocID { @@ -283,14 +282,14 @@ func (db *commonMsgDatabase) BatchInsertBlock(ctx context.Context, conversationI } if err := db.msgDocDatabase.Create(ctx, &doc); err != nil { if mongo.IsDuplicateKeyError(err) { - i-- // 存在并发,重试当前数据 - tryUpdate = true // 以修改模式 + i-- // already inserted + tryUpdate = true // next block use update mode continue } return err } - tryUpdate = false // 当前以插入成功,下一块优先插入模式 - i += insert - 1 // 跳过已插入的数据 + tryUpdate = false // The current block is inserted successfully, and the next block is inserted preferentially + i += insert - 1 // Skip the inserted data } return nil } @@ -754,7 +753,7 @@ func (db *commonMsgDatabase) UserMsgsDestruct(ctx context.Context, userID string log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index) } } - // 获取报错,或者获取不到了,物理删除并且返回seq delMongoMsgsPhysical(delStruct.delDocIDList), 结束递归 + // If an error is reported, or the error cannot be obtained, it is physically deleted and seq delMongoMsgsPhysical(delStruct.delDocIDList) is returned to end the recursion break } index++ @@ -809,7 +808,7 @@ func (d *delMsgRecursionStruct) getSetMinSeq() int64 { // index 0....19(del) 20...69 // seq 70 // set minSeq 21 -// recursion 删除list并且返回设置的最小seq. +// recursion deletes the list and returns the set minimum seq. func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversationID string, index int64, delStruct *delMsgRecursionStruct, remainTime int64) (int64, error) { // find from oldest list msgDocModel, err := db.msgDocDatabase.GetMsgDocModelByIndex(ctx, conversationID, index, 1) @@ -821,7 +820,7 @@ func (db *commonMsgDatabase) deleteMsgRecursion(ctx context.Context, conversatio log.ZError(ctx, "deleteMsgRecursion GetUserMsgListByIndex failed", err, "conversationID", conversationID, "index", index) } } - // 获取报错,或者获取不到了,物理删除并且返回seq delMongoMsgsPhysical(delStruct.delDocIDList), 结束递归 + // If an error is reported, or the error cannot be obtained, it is physically deleted and seq delMongoMsgsPhysical(delStruct.delDocIDList) is returned to end the recursion err = db.msgDocDatabase.DeleteDocs(ctx, delStruct.delDocIDs) if err != nil { return 0, err diff --git a/pkg/common/db/controller/third.go b/pkg/common/db/controller/third.go index fb5b0ccbe..55c047bd6 100644 --- a/pkg/common/db/controller/third.go +++ b/pkg/common/db/controller/third.go @@ -63,13 +63,7 @@ func NewThirdDatabase(cache cache.MsgModel, logdb relation.LogInterface) ThirdDa return &thirdDatabase{cache: cache, logdb: logdb} } -func (t *thirdDatabase) FcmUpdateToken( - ctx context.Context, - account string, - platformID int, - fcmToken string, - expireTime int64, -) error { +func (t *thirdDatabase) FcmUpdateToken(ctx context.Context, account string, platformID int, fcmToken string, expireTime int64) error { return t.cache.SetFcmToken(ctx, account, platformID, fcmToken, expireTime) } diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go index d996485c7..7588eea91 100644 --- a/pkg/common/db/s3/aws/aws.go +++ b/pkg/common/db/s3/aws/aws.go @@ -58,10 +58,10 @@ func NewAWS() (s3.Interface, error) { credential := credentials.NewStaticCredentials( conf.AccessKeyID, // accessKey conf.AccessKeySecret, // secretKey - "") // sts的临时凭证 + "") // stoken sess, err := session.NewSession(&aws.Config{ - Region: aws.String(conf.Region), // 桶所在的区域 + Region: aws.String(conf.Region), // The area where the bucket is located Credentials: credential, }) diff --git a/pkg/common/db/s3/cont/consts.go b/pkg/common/db/s3/cont/consts.go index a01a8312c..b51878e49 100644 --- a/pkg/common/db/s3/cont/consts.go +++ b/pkg/common/db/s3/cont/consts.go @@ -15,10 +15,24 @@ package cont const ( - hashPath = "openim/data/hash/" - tempPath = "openim/temp/" - DirectPath = "openim/direct" - UploadTypeMultipart = 1 // 分片上传 - UploadTypePresigned = 2 // 预签名上传 - partSeparator = "," + // hashPath defines the storage path for hash data within the 'openim' directory. + hashPath = "openim/data/hash/" + + // tempPath specifies the directory for temporary files in the 'openim' structure. + tempPath = "openim/temp/" + + // DirectPath indicates the directory for direct uploads or access within the 'openim' structure. + DirectPath = "openim/direct" + + // UploadTypeMultipart represents the identifier for multipart uploads, + // allowing large files to be uploaded in chunks. + UploadTypeMultipart = 1 + + // UploadTypePresigned signifies the use of presigned URLs for uploads, + // facilitating secure, authorized file transfers without requiring direct access to the storage credentials. + UploadTypePresigned = 2 + + // partSeparator is used as a delimiter in multipart upload processes, + // separating individual file parts. + partSeparator = "," ) diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go index 2a66aeaf6..f60c2c650 100644 --- a/pkg/common/db/s3/cont/controller.go +++ b/pkg/common/db/s3/cont/controller.go @@ -114,7 +114,7 @@ func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64 return nil, err } if size <= partSize { - // 预签名上传 + // Pre-signed upload key := path.Join(tempPath, c.NowPath(), fmt.Sprintf("%s_%d_%s.presigned", hash, size, c.UUID())) rawURL, err := c.impl.PresignedPutObject(ctx, key, expire) if err != nil { @@ -139,7 +139,7 @@ func (c *Controller) InitiateUpload(ctx context.Context, hash string, size int64 }, }, nil } else { - // 分片上传 + // Fragment upload upload, err := c.impl.InitiateMultipartUpload(ctx, c.HashPath(hash)) if err != nil { return nil, err @@ -206,7 +206,7 @@ func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHa ETag: part, } } - // todo: 验证大小 + // todo: Validation size result, err := c.impl.CompleteMultipartUpload(ctx, upload.ID, upload.Key, parts) if err != nil { return nil, err @@ -225,7 +225,7 @@ func (c *Controller) CompleteUpload(ctx context.Context, uploadID string, partHa if md5val := hex.EncodeToString(md5Sum[:]); md5val != upload.Hash { return nil, errs.ErrArgs.Wrap(fmt.Sprintf("md5 mismatching %s != %s", md5val, upload.Hash)) } - // 防止在这个时候,并发操作,导致文件被覆盖 + // Prevents concurrent operations at this time that cause files to be overwritten copyInfo, err := c.impl.CopyObject(ctx, uploadInfo.Key, upload.Key+"."+c.UUID()) if err != nil { return nil, err diff --git a/pkg/common/db/s3/cont/structs.go b/pkg/common/db/s3/cont/structs.go index ff5ca7943..de484cc5f 100644 --- a/pkg/common/db/s3/cont/structs.go +++ b/pkg/common/db/s3/cont/structs.go @@ -17,9 +17,14 @@ package cont import "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" type InitiateUploadResult struct { - UploadID string `json:"uploadID"` // 上传ID - PartSize int64 `json:"partSize"` // 分片大小 - Sign *s3.AuthSignResult `json:"sign"` // 分片信息 + // UploadID uniquely identifies the upload session for tracking and management purposes. + UploadID string `json:"uploadID"` + + // PartSize specifies the size of each part in a multipart upload. This is relevant for breaking down large uploads into manageable pieces. + PartSize int64 `json:"partSize"` + + // Sign contains the authentication and signature information necessary for securely uploading each part. This could include signed URLs or tokens. + Sign *s3.AuthSignResult `json:"sign"` } type UploadResult struct { diff --git a/pkg/common/db/s3/minio/image.go b/pkg/common/db/s3/minio/image.go index 71db1ea51..f363f94b1 100644 --- a/pkg/common/db/s3/minio/image.go +++ b/pkg/common/db/s3/minio/image.go @@ -42,79 +42,51 @@ func ImageWidthHeight(img image.Image) (int, int) { return bounds.X, bounds.Y } +// resizeImage resizes an image to a specified maximum width and height, maintaining the aspect ratio. +// If both maxWidth and maxHeight are set to 0, the original image is returned. +// If both are non-zero, the image is scaled to fit within the constraints while maintaining aspect ratio. +// If only one of maxWidth or maxHeight is non-zero, the image is scaled accordingly. func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { bounds := img.Bounds() - imgWidth := bounds.Max.X - imgHeight := bounds.Max.Y + imgWidth, imgHeight := bounds.Dx(), bounds.Dy() - // 计算缩放比例 - scaleWidth := float64(maxWidth) / float64(imgWidth) - scaleHeight := float64(maxHeight) / float64(imgHeight) - - // 如果都为0,则不缩放,返回原始图片 + // Return original image if no resizing is needed. if maxWidth == 0 && maxHeight == 0 { return img } - // 如果宽度和高度都大于0,则选择较小的缩放比例,以保持宽高比 + var scale float64 = 1 if maxWidth > 0 && maxHeight > 0 { - scale := scaleWidth - if scaleHeight < scaleWidth { - scale = scaleHeight - } - - // 计算缩略图尺寸 - thumbnailWidth := int(float64(imgWidth) * scale) - thumbnailHeight := int(float64(imgHeight) * scale) - - // 使用"image"库的Resample方法生成缩略图 - thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) - for y := 0; y < thumbnailHeight; y++ { - for x := 0; x < thumbnailWidth; x++ { - srcX := int(float64(x) / scale) - srcY := int(float64(y) / scale) - thumbnail.Set(x, y, img.At(srcX, srcY)) - } - } - - return thumbnail + scaleWidth := float64(maxWidth) / float64(imgWidth) + scaleHeight := float64(maxHeight) / float64(imgHeight) + // Choose the smaller scale to fit both constraints. + scale = min(scaleWidth, scaleHeight) + } else if maxWidth > 0 { + scale = float64(maxWidth) / float64(imgWidth) + } else if maxHeight > 0 { + scale = float64(maxHeight) / float64(imgHeight) } - // 如果只指定了宽度或高度,则根据最大不超过的规则生成缩略图 - if maxWidth > 0 { - thumbnailWidth := maxWidth - thumbnailHeight := int(float64(imgHeight) * scaleWidth) + newWidth := int(float64(imgWidth) * scale) + newHeight := int(float64(imgHeight) * scale) - // 使用"image"库的Resample方法生成缩略图 - thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) - for y := 0; y < thumbnailHeight; y++ { - for x := 0; x < thumbnailWidth; x++ { - srcX := int(float64(x) / scaleWidth) - srcY := int(float64(y) / scaleWidth) - thumbnail.Set(x, y, img.At(srcX, srcY)) - } + // Resize the image by creating a new image and manually copying pixels. + thumbnail := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) + for y := 0; y < newHeight; y++ { + for x := 0; x < newWidth; x++ { + srcX := int(float64(x) / scale) + srcY := int(float64(y) / scale) + thumbnail.Set(x, y, img.At(srcX, srcY)) } - - return thumbnail } - if maxHeight > 0 { - thumbnailWidth := int(float64(imgWidth) * scaleHeight) - thumbnailHeight := maxHeight - - // 使用"image"库的Resample方法生成缩略图 - thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) - for y := 0; y < thumbnailHeight; y++ { - for x := 0; x < thumbnailWidth; x++ { - srcX := int(float64(x) / scaleHeight) - srcY := int(float64(y) / scaleHeight) - thumbnail.Set(x, y, img.At(srcX, srcY)) - } - } + return thumbnail +} - return thumbnail +// min returns the smaller of x or y. +func min(x, y float64) float64 { + if x < y { + return x } - - // 默认情况下,返回原始图片 - return img + return y } diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go index 98473b87f..a6be41d39 100644 --- a/pkg/common/db/s3/oss/oss.go +++ b/pkg/common/db/s3/oss/oss.go @@ -30,6 +30,7 @@ import ( "strings" "time" + "github.com/OpenIMSDK/tools/errs" "github.com/aliyun/aliyun-oss-go-sdk/oss" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -60,7 +61,7 @@ const successCode = http.StatusOK func NewOSS() (s3.Interface, error) { conf := config.Config.Object.Oss if conf.BucketURL == "" { - return nil, errors.New("bucket url is empty") + return nil, errs.Wrap(errors.New("bucket url is empty")) } client, err := oss.New(conf.Endpoint, conf.AccessKeyID, conf.AccessKeySecret) if err != nil { @@ -68,7 +69,7 @@ func NewOSS() (s3.Interface, error) { } bucket, err := client.Bucket(conf.Bucket) if err != nil { - return nil, err + return nil, errs.Wrap(err, "ali-oss bucket error") } if conf.BucketURL[len(conf.BucketURL)-1] != '/' { conf.BucketURL += "/" @@ -138,10 +139,10 @@ func (o *OSS) CompleteMultipartUpload(ctx context.Context, uploadID string, name func (o *OSS) PartSize(ctx context.Context, size int64) (int64, error) { if size <= 0 { - return 0, errors.New("size must be greater than 0") + return 0, errs.Wrap(errors.New("size must be greater than 0")) } if size > maxPartSize*maxNumSize { - return 0, fmt.Errorf("OSS size must be less than the maximum allowed limit") + return 0, errs.Wrap(errors.New("size must be less than the maximum allowed limit")) } if size <= minPartSize*maxNumSize { return minPartSize, nil @@ -196,25 +197,25 @@ func (o *OSS) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, erro } res := &s3.ObjectInfo{Key: name} if res.ETag = strings.ToLower(strings.ReplaceAll(header.Get("ETag"), `"`, ``)); res.ETag == "" { - return nil, errors.New("StatObject etag not found") + return nil, errs.Wrap(errors.New("StatObject etag not found")) } if contentLengthStr := header.Get("Content-Length"); contentLengthStr == "" { return nil, errors.New("StatObject content-length not found") } else { res.Size, err = strconv.ParseInt(contentLengthStr, 10, 64) if err != nil { - return nil, fmt.Errorf("StatObject content-length parse error: %w", err) + return nil, errs.Wrap(err, "StatObject content-length parse error") } if res.Size < 0 { - return nil, errors.New("StatObject content-length must be greater than 0") + return nil, errs.Wrap(errors.New("StatObject content-length must be greater than 0")) } } if lastModified := header.Get("Last-Modified"); lastModified == "" { - return nil, errors.New("StatObject last-modified not found") + return nil, errs.Wrap(errors.New("StatObject last-modified not found")) } else { res.LastModified, err = time.Parse(http.TimeFormat, lastModified) if err != nil { - return nil, fmt.Errorf("StatObject last-modified parse error: %w", err) + return nil, errs.Wrap(err, "StatObject last-modified parse error") } } return res, nil @@ -227,7 +228,7 @@ func (o *OSS) DeleteObject(ctx context.Context, name string) error { func (o *OSS) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { result, err := o.bucket.CopyObject(src, dst) if err != nil { - return nil, err + return nil, errs.Wrap(err, "CopyObject error") } return &s3.CopyObjectInfo{ Key: dst, @@ -261,7 +262,7 @@ func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name strin Bucket: o.bucket.BucketName, }, oss.MaxUploads(100), oss.MaxParts(maxParts), oss.PartNumberMarker(partNumberMarker)) if err != nil { - return nil, err + return nil, errs.Wrap(err, "ListUploadedParts error") } res := &s3.ListUploadedPartsResult{ Key: result.Key, @@ -286,7 +287,7 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, var opts []oss.Option if opt != nil { if opt.Image != nil { - // 文档地址: https://help.aliyun.com/zh/oss/user-guide/resize-images-4?spm=a2c4g.11186623.0.0.4b3b1e4fWW6yji + // Docs Address: https://help.aliyun.com/zh/oss/user-guide/resize-images-4?spm=a2c4g.11186623.0.0.4b3b1e4fWW6yji var format string switch opt.Image.Format { case @@ -329,7 +330,7 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, } rawParams, err := oss.GetRawParams(opts) if err != nil { - return "", err + return "", errs.Wrap(err, "AccessURL error") } params := getURLParams(*o.bucket.Client.Conn, rawParams) return getURL(o.um, o.bucket.BucketName, name, params).String(), nil @@ -351,12 +352,12 @@ func (o *OSS) FormData(ctx context.Context, name string, size int64, contentType } policyJson, err := json.Marshal(policy) if err != nil { - return nil, err + return nil, errs.Wrap(err, "Marshal json error") } policyStr := base64.StdEncoding.EncodeToString(policyJson) h := hmac.New(sha1.New, []byte(o.credentials.GetAccessKeySecret())) if _, err := io.WriteString(h, policyStr); err != nil { - return nil, err + return nil, errs.Wrap(err, "WriteString error") } fd := &s3.FormData{ URL: o.bucketURL, diff --git a/pkg/common/db/table/relation/group.go b/pkg/common/db/table/relation/group.go index 57d6b1d62..1f969cd4f 100644 --- a/pkg/common/db/table/relation/group.go +++ b/pkg/common/db/table/relation/group.go @@ -46,8 +46,8 @@ type GroupModelInterface interface { Find(ctx context.Context, groupIDs []string) (groups []*GroupModel, err error) Take(ctx context.Context, groupID string) (group *GroupModel, err error) Search(ctx context.Context, keyword string, pagination pagination.Pagination) (total int64, groups []*GroupModel, err error) - // 获取群总数 + // Get Group total quantity CountTotal(ctx context.Context, before *time.Time) (count int64, err error) - // 获取范围内群增量 + // Get Group total quantity every day CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index dbb2ff464..265c3c93a 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -62,9 +62,9 @@ type UserModelInterface interface { Exist(ctx context.Context, userID string) (exist bool, err error) GetAllUserID(ctx context.Context, pagination pagination.Pagination) (count int64, userIDs []string, err error) GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) - // 获取用户总数 + // Get user total quantity CountTotal(ctx context.Context, before *time.Time) (count int64, err error) - // 获取范围内用户增量 + // Get user total quantity every day CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) //CRUD user command AddUserCommand(ctx context.Context, userID string, Type int32, UUID string, value string, ex string) error diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index bc9118a9a..0a6f683a5 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -21,8 +21,6 @@ import ( "fmt" "time" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/constant" @@ -35,6 +33,7 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) @@ -122,13 +121,7 @@ func (m *MsgMongoDriver) UpdateMsgContent(ctx context.Context, docID string, ind return nil } -func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc( - ctx context.Context, - docID string, - msg *sdkws.MsgData, - seqIndex int, - status int32, -) error { +func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc(ctx context.Context, docID string, msg *sdkws.MsgData, seqIndex int, status int32) error { msg.Status = status bytes, err := proto.Marshal(msg) if err != nil { @@ -140,7 +133,7 @@ func (m *MsgMongoDriver) UpdateMsgStatusByIndexInOneDoc( bson.M{"$set": bson.M{fmt.Sprintf("msgs.%d.msg", seqIndex): bytes}}, ) if err != nil { - return errs.Wrap(err) + return errs.Wrap(err, fmt.Sprintf("docID is %s, seqIndex is %d", docID, seqIndex)) } return nil } @@ -166,7 +159,7 @@ func (m *MsgMongoDriver) GetMsgDocModelByIndex( findOpts, ) if err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, fmt.Sprintf("conversationID is %s", conversationID)) } var msgs []table.MsgDocModel err = cursor.All(ctx, &msgs) @@ -222,7 +215,7 @@ func (m *MsgMongoDriver) DeleteMsgsInOneDocByIndex(ctx context.Context, docID st } _, err := m.MsgCollection.UpdateMany(ctx, bson.M{"doc_id": docID}, updates) if err != nil { - return errs.Wrap(err) + return errs.Wrap(err, fmt.Sprintf("docID is %s, indexes is %v", docID, indexes)) } return nil } @@ -289,7 +282,7 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( defer cur.Close(ctx) var msgDocModel []table.MsgDocModel if err := cur.All(ctx, &msgDocModel); err != nil { - return nil, errs.Wrap(err) + return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs)) } if len(msgDocModel) == 0 { return nil, errs.Wrap(mongo.ErrNoDocuments) @@ -316,14 +309,14 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( } data, err := json.Marshal(&revokeContent) if err != nil { - return nil, err + return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs)) } elem := sdkws.NotificationElem{ Detail: string(data), } content, err := json.Marshal(&elem) if err != nil { - return nil, err + return nil, errs.Wrap(err, fmt.Sprintf("docID is %s, seqs is %v", docID, seqs)) } msg.Msg.ContentType = constant.MsgRevokeNotification msg.Msg.Content = string(content) @@ -336,17 +329,12 @@ func (m *MsgMongoDriver) GetMsgBySeqIndexIn1Doc( func (m *MsgMongoDriver) IsExistDocID(ctx context.Context, docID string) (bool, error) { count, err := m.MsgCollection.CountDocuments(ctx, bson.M{"doc_id": docID}) if err != nil { - return false, errs.Wrap(err) + return false, errs.Wrap(err, fmt.Sprintf("docID is %s", docID)) } return count > 0, nil } -func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead( - ctx context.Context, - userID string, - docID string, - indexes []int64, -) error { +func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead(ctx context.Context, userID string, docID string, indexes []int64) error { updates := []mongo.WriteModel{} for _, index := range indexes { filter := bson.M{ @@ -366,7 +354,7 @@ func (m *MsgMongoDriver) MarkSingleChatMsgsAsRead( updates = append(updates, updateModel) } _, err := m.MsgCollection.BulkWrite(ctx, updates) - return err + return errs.Wrap(err, fmt.Sprintf("docID is %s, indexes is %v", docID, indexes)) } // RangeUserSendCount @@ -662,7 +650,7 @@ func (m *MsgMongoDriver) RangeUserSendCount( "$dateToString": bson.M{ "format": "%Y-%m-%d", "date": bson.M{ - "$toDate": "$$item.msg.send_time", // 毫秒时间戳 + "$toDate": "$$item.msg.send_time", // Millisecond timestamp }, }, }, @@ -911,7 +899,7 @@ func (m *MsgMongoDriver) RangeGroupSendCount( "$dateToString": bson.M{ "format": "%Y-%m-%d", "date": bson.M{ - "$toDate": "$$item.msg.send_time", // 毫秒时间戳 + "$toDate": "$$item.msg.send_time", // Millisecond timestamp }, }, }, @@ -1076,6 +1064,7 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa var pipe mongo.Pipeline condition := bson.A{} if req.SendTime != "" { + // Changed to keyed fields for bson.M to avoid govet errors condition = append(condition, bson.M{"$eq": bson.A{bson.M{"$dateToString": bson.M{"format": "%Y-%m-%d", "date": bson.M{"$toDate": "$$item.msg.send_time"}}}, req.SendTime}}) } if req.MsgType != 0 { @@ -1092,62 +1081,26 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa } or := bson.A{ - bson.M{ - "doc_id": bson.M{ - "$regex": "^si_", - "$options": "i", - }, - }, + bson.M{"doc_id": bson.M{"$regex": "^si_", "$options": "i"}}, + bson.M{"doc_id": bson.M{"$regex": "^g_", "$options": "i"}}, + bson.M{"doc_id": bson.M{"$regex": "^sg_", "$options": "i"}}, } - or = append(or, - bson.M{ - "doc_id": bson.M{ - "$regex": "^g_", - "$options": "i", - }, - }, - bson.M{ - "doc_id": bson.M{ - "$regex": "^sg_", - "$options": "i", - }, - }, - ) + // Use bson.D with keyed fields to specify the order explicitly pipe = mongo.Pipeline{ - { - {"$match", bson.D{ - { - "$or", or, - }, - }}, - }, - { - {"$project", bson.D{ - { - "msgs", bson.D{ - { - "$filter", bson.D{ - {"input", "$msgs"}, - {"as", "item"}, - { - "cond", bson.D{ - {"$and", condition}, - }, - }, - }, - }, - }, - }, - {"doc_id", 1}, + {{"$match", bson.D{{Key: "$or", Value: or}}}}, + {{"$project", bson.D{ + {Key: "msgs", Value: bson.D{ + {Key: "$filter", Value: bson.D{ + {Key: "input", Value: "$msgs"}, + {Key: "as", Value: "item"}, + {Key: "cond", Value: bson.D{{Key: "$and", Value: condition}}}, + }}, }}, - }, - { - {"$unwind", bson.M{"path": "$msgs"}}, - }, - { - {"$sort", bson.M{"msgs.msg.send_time": -1}}, - }, + {Key: "doc_id", Value: 1}, + }}}, + {{"$unwind", bson.M{"path": "$msgs"}}}, + {{"$sort", bson.M{"msgs.msg.send_time": -1}}}, } cursor, err := m.MsgCollection.Aggregate(ctx, pipe) if err != nil { @@ -1160,12 +1113,12 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa var msgsDocs []docModel err = cursor.All(ctx, &msgsDocs) if err != nil { - return 0, nil, err + return 0, nil, errs.Wrap(err, "cursor.All msgsDocs") } log.ZDebug(ctx, "query mongoDB", "result", msgsDocs) msgs := make([]*table.MsgInfoModel, 0) - for index := range msgsDocs { - msgInfo := msgsDocs[index].Msg + for _, doc := range msgsDocs { + msgInfo := doc.Msg if msgInfo == nil || msgInfo.Msg == nil { continue } @@ -1185,14 +1138,12 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa } data, err := json.Marshal(&revokeContent) if err != nil { - return 0, nil, err - } - elem := sdkws.NotificationElem{ - Detail: string(data), + return 0, nil, errs.Wrap(err, "json.Marshal revokeContent") } + elem := sdkws.NotificationElem{Detail: string(data)} content, err := json.Marshal(&elem) if err != nil { - return 0, nil, err + return 0, nil, errs.Wrap(err, "json.Marshal elem") } msgInfo.Msg.ContentType = constant.MsgRevokeNotification msgInfo.Msg.Content = string(content) @@ -1203,7 +1154,8 @@ func (m *MsgMongoDriver) searchMessage(ctx context.Context, req *msg.SearchMessa n := int32(len(msgs)) if start >= n { return n, []*table.MsgInfoModel{}, nil - } else if start+req.Pagination.ShowNumber < n { + } + if start+req.Pagination.ShowNumber < n { msgs = msgs[start : start+req.Pagination.ShowNumber] } else { msgs = msgs[start:] diff --git a/pkg/common/discoveryregister/direct/directconn.go b/pkg/common/discoveryregister/direct/directconn.go index 84f173ea6..cc59934a3 100644 --- a/pkg/common/discoveryregister/direct/directconn.go +++ b/pkg/common/discoveryregister/direct/directconn.go @@ -105,7 +105,7 @@ func (cd *ConnDirect) GetConns(ctx context.Context, } if len(connections) == 0 { - return nil, fmt.Errorf("no connections found for service: %s", serviceName) + return nil, errs.Wrap(errors.New("no connections found for service"), "serviceName", serviceName) } return connections, nil } @@ -155,10 +155,11 @@ func (cd *ConnDirect) dialService(ctx context.Context, address string, opts ...g conn, err := grpc.DialContext(ctx, cd.resolverDirect.Scheme()+":///"+address, options...) if err != nil { - return nil, err + return nil, errs.Wrap(err, "address", address) } return conn, nil } + func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { options := append(opts, grpc.WithTransportCredentials(insecure.NewCredentials())) conn, err := grpc.DialContext(ctx, address, options...) diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go index 23a9e3245..4ade0147a 100644 --- a/pkg/common/discoveryregister/discoveryregister.go +++ b/pkg/common/discoveryregister/discoveryregister.go @@ -24,6 +24,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" ) // NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type. @@ -41,6 +42,6 @@ func NewDiscoveryRegister(envType string) (discoveryregistry.SvcDiscoveryRegistr case "direct": return direct.NewConnDirect() default: - return nil, errors.New("envType not correct") + return nil, errs.Wrap(errors.New("envType not correct")) } } diff --git a/pkg/common/discoveryregister/discoveryregister_test.go b/pkg/common/discoveryregister/discoveryregister_test.go index 5317db5c6..e7a5fb276 100644 --- a/pkg/common/discoveryregister/discoveryregister_test.go +++ b/pkg/common/discoveryregister/discoveryregister_test.go @@ -39,7 +39,7 @@ func TestNewDiscoveryRegister(t *testing.T) { expectedResult bool }{ {"zookeeper", false, true}, - {"k8s", false, true}, // 假设 k8s 配置也已正确设置 + {"k8s", false, true}, // Assume that the k8s configuration is also set up correctly {"direct", false, true}, {"invalid", true, false}, } diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go index 7fc456a1d..141284b64 100644 --- a/pkg/common/http/http_client.go +++ b/pkg/common/http/http_client.go @@ -66,12 +66,12 @@ func Post(ctx context.Context, url string, header map[string]string, data any, t jsonStr, err := json.Marshal(data) if err != nil { - return nil, err + return nil, errs.Wrap(err, "Post: JSON marshal failed") } req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonStr)) if err != nil { - return nil, err + return nil, errs.Wrap(err, "Post: NewRequestWithContext failed") } if operationID, _ := ctx.Value(constant.OperationID).(string); operationID != "" { @@ -84,13 +84,13 @@ func Post(ctx context.Context, url string, header map[string]string, data any, t resp, err := client.Do(req) if err != nil { - return nil, err + return nil, errs.Wrap(err, "Post: client.Do failed") } defer resp.Body.Close() result, err := io.ReadAll(resp.Body) if err != nil { - return nil, err + return nil, errs.Wrap(err, "Post: ReadAll failed") } return result, nil @@ -102,7 +102,10 @@ func PostReturn(ctx context.Context, url string, header map[string]string, input return err } err = json.Unmarshal(b, output) - return err + if err != nil { + return errs.Wrap(err, "PostReturn: JSON unmarshal failed") + } + return nil } func callBackPostReturn(ctx context.Context, url, command string, input interface{}, output callbackstruct.CallbackResp, callbackConfig config.CallBackConfig) error { @@ -127,7 +130,6 @@ func callBackPostReturn(ctx context.Context, url, command string, input interfac } if err := output.Parse(); err != nil { log.ZWarn(ctx, "callback parse failed", err, "url", url, "input", input, "response", string(b)) - return err } log.ZInfo(ctx, "callback success", "url", url, "input", input, "response", string(b)) return nil diff --git a/pkg/common/kafka/consumer.go b/pkg/common/kafka/consumer.go index 98adfdcf1..d0e06d482 100644 --- a/pkg/common/kafka/consumer.go +++ b/pkg/common/kafka/consumer.go @@ -17,9 +17,8 @@ package kafka import ( "sync" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/IBM/sarama" + "github.com/OpenIMSDK/tools/errs" ) type Consumer struct { @@ -30,28 +29,33 @@ type Consumer struct { Consumer sarama.Consumer } -func NewKafkaConsumer(addr []string, topic string) *Consumer { - p := Consumer{} - p.Topic = topic - p.addr = addr - consumerConfig := sarama.NewConfig() - if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" { - consumerConfig.Net.SASL.Enable = true - consumerConfig.Net.SASL.User = config.Config.Kafka.Username - consumerConfig.Net.SASL.Password = config.Config.Kafka.Password +func NewKafkaConsumer(addr []string, topic string, kafkaConfig *sarama.Config) (*Consumer, error) { + p := Consumer{ + Topic: topic, + addr: addr, + } + + if kafkaConfig.Net.SASL.User != "" && kafkaConfig.Net.SASL.Password != "" { + kafkaConfig.Net.SASL.Enable = true + } + + err := SetupTLSConfig(kafkaConfig) + if err != nil { + return nil, err } - SetupTLSConfig(consumerConfig) - consumer, err := sarama.NewConsumer(p.addr, consumerConfig) + + consumer, err := sarama.NewConsumer(p.addr, kafkaConfig) if err != nil { - panic(err.Error()) + return nil, errs.Wrap(err, "NewKafkaConsumer: creating consumer failed") } p.Consumer = consumer partitionList, err := consumer.Partitions(p.Topic) if err != nil { - panic(err.Error()) + return nil, errs.Wrap(err, "NewKafkaConsumer: getting partitions failed") } p.PartitionList = partitionList - return &p + return &p, nil + } diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index bdb839404..4b5ce6b52 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -90,11 +90,10 @@ func NewKafkaProducer(addr []string, topic string) (*Producer, error) { for i := 0; i <= maxRetry; i++ { p.producer, err = sarama.NewSyncProducer(p.addr, p.config) if err == nil { - return &p, nil + return &p, errs.Wrap(err) } time.Sleep(1 * time.Second) // Wait before retrying } - // Panic if unable to create producer after retries if err != nil { return nil, errs.Wrap(errors.New("failed to create Kafka producer: " + err.Error())) @@ -179,7 +178,7 @@ func (p *Producer) SendMessage(ctx context.Context, key string, msg proto.Messag // Attach context metadata as headers header, err := GetMQHeaderWithContext(ctx) if err != nil { - return 0, 0, errs.Wrap(err) + return 0, 0, err } kMsg.Headers = header diff --git a/pkg/common/kafka/util.go b/pkg/common/kafka/util.go index f318ecf73..578a308ef 100644 --- a/pkg/common/kafka/util.go +++ b/pkg/common/kafka/util.go @@ -26,16 +26,21 @@ import ( ) // SetupTLSConfig set up the TLS config from config file. -func SetupTLSConfig(cfg *sarama.Config) { +func SetupTLSConfig(cfg *sarama.Config) error { if config.Config.Kafka.TLS != nil { cfg.Net.TLS.Enable = true - cfg.Net.TLS.Config = tls.NewTLSConfig( + tlsConfig, err := tls.NewTLSConfig( config.Config.Kafka.TLS.ClientCrt, config.Config.Kafka.TLS.ClientKey, config.Config.Kafka.TLS.CACrt, []byte(config.Config.Kafka.TLS.ClientKeyPwd), ) + if err != nil { + return err + } + cfg.Net.TLS.Config = tlsConfig } + return nil } // getEnvOrConfig returns the value of the environment variable if it exists, diff --git a/pkg/common/prommetrics/prommetrics.go b/pkg/common/prommetrics/prommetrics.go index b7c5e07f4..1ed13a1ef 100644 --- a/pkg/common/prommetrics/prommetrics.go +++ b/pkg/common/prommetrics/prommetrics.go @@ -24,7 +24,6 @@ import ( ) func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *gp.ServerMetrics, error) { - //////////////////////////////////////////////////////// reg := prometheus.NewRegistry() grpcMetrics := gp.NewServerMetrics() grpcMetrics.EnableHandlingTimeHistogram() diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index be9fbd25b..e19456d86 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -70,7 +70,7 @@ func Start( defer listener.Close() client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) if err != nil { - return errs.Wrap(err) + return err } defer client.Close() diff --git a/pkg/common/tls/tls.go b/pkg/common/tls/tls.go old mode 100755 new mode 100644 index dba49e605..e897c13b6 --- a/pkg/common/tls/tls.go +++ b/pkg/common/tls/tls.go @@ -21,6 +21,8 @@ import ( "errors" "os" + "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) @@ -49,37 +51,41 @@ func readEncryptablePEMBlock(path string, pwd []byte) ([]byte, error) { } // NewTLSConfig setup the TLS config from general config file. -func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte) *tls.Config { - tlsConfig := tls.Config{} +func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte) (*tls.Config, error) { + var tlsConfig tls.Config if clientCertFile != "" && clientKeyFile != "" { certPEMBlock, err := os.ReadFile(clientCertFile) if err != nil { - panic(err) + return nil, errs.Wrap(err, "NewTLSConfig: failed to read client cert file") } + keyPEMBlock, err := readEncryptablePEMBlock(clientKeyFile, keyPwd) if err != nil { - panic(err) + return nil, err } + cert, err := tls.X509KeyPair(certPEMBlock, keyPEMBlock) if err != nil { - panic(err) + return nil, errs.Wrap(err, "NewTLSConfig: failed to create X509 key pair") } tlsConfig.Certificates = []tls.Certificate{cert} } - caCert, err := os.ReadFile(caCertFile) - if err != nil { - panic(err) - } - caCertPool := x509.NewCertPool() - ok := caCertPool.AppendCertsFromPEM(caCert) - if !ok { - panic(errors.New("not a valid CA cert")) + if caCertFile != "" { + caCert, err := os.ReadFile(caCertFile) + if err != nil { + return nil, errs.Wrap(err, "NewTLSConfig: failed to read CA cert file") + } + + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(caCert); !ok { + return nil, errors.New("NewTLSConfig: not a valid CA cert") + } + tlsConfig.RootCAs = caCertPool } - tlsConfig.RootCAs = caCertPool tlsConfig.InsecureSkipVerify = config.Config.Kafka.TLS.InsecureSkipVerify - return &tlsConfig + return &tlsConfig, nil } diff --git a/pkg/msgprocessor/conversation.go b/pkg/msgprocessor/conversation.go index 7477bea7a..e3ea89fad 100644 --- a/pkg/msgprocessor/conversation.go +++ b/pkg/msgprocessor/conversation.go @@ -20,6 +20,7 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" "google.golang.org/protobuf/proto" ) @@ -188,7 +189,7 @@ func (s MsgBySeq) Swap(i, j int) { func Pb2String(pb proto.Message) (string, error) { s, err := proto.Marshal(pb) if err != nil { - return "", err + return "", errs.Wrap(err) } return string(s), nil } diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index 0ee021de1..810b7ae93 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -23,12 +23,13 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry) *Auth { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImAuthName) if err != nil { - panic(err) + util.ExitWithError(err) } client := auth.NewAuthClient(conn) return &Auth{discov: discov, conn: conn, Client: client} diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 3ba8dd8c0..9afa45004 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -24,6 +24,8 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) @@ -36,7 +38,7 @@ type Conversation struct { func NewConversation(discov discoveryregistry.SvcDiscoveryRegistry) *Conversation { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImConversationName) if err != nil { - panic(err) + util.ExitWithError(err) } client := pbconversation.NewConversationClient(conn) return &Conversation{discov: discov, conn: conn, Client: client} @@ -114,11 +116,7 @@ func (c *ConversationRpcClient) GetConversationsByConversationID(ctx context.Con return resp.Conversations, nil } -func (c *ConversationRpcClient) GetConversations( - ctx context.Context, - ownerUserID string, - conversationIDs []string, -) ([]*pbconversation.Conversation, error) { +func (c *ConversationRpcClient) GetConversations(ctx context.Context, ownerUserID string, conversationIDs []string) ([]*pbconversation.Conversation, error) { if len(conversationIDs) == 0 { return nil, nil } diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index b84db40d4..39026b10c 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -24,6 +24,7 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type Friend struct { @@ -35,7 +36,7 @@ type Friend struct { func NewFriend(discov discoveryregistry.SvcDiscoveryRegistry) *Friend { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImFriendName) if err != nil { - panic(err) + util.ExitWithError(err) } client := friend.NewFriendClient(conn) return &Friend{discov: discov, conn: conn, Client: client} @@ -62,7 +63,7 @@ func (f *FriendRpcClient) GetFriendsInfo( return } -// possibleFriendUserID是否在userID的好友中. +// possibleFriendUserID Is PossibleFriendUserId's friends. func (f *FriendRpcClient) IsFriend(ctx context.Context, possibleFriendUserID, userID string) (bool, error) { resp, err := f.Client.IsFriend(ctx, &friend.IsFriendReq{UserID1: userID, UserID2: possibleFriendUserID}) if err != nil { diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index bf0efe60c..cb61579be 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -28,6 +28,7 @@ import ( "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type Group struct { @@ -39,7 +40,7 @@ type Group struct { func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry) *Group { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImGroupName) if err != nil { - panic(err) + util.ExitWithError(err) } client := group.NewGroupClient(conn) return &Group{discov: discov, conn: conn, Client: client} diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 56167d7f4..6804e78a2 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -147,14 +147,24 @@ func NewMessageRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) MessageR return MessageRpcClient(*NewMessage(discov)) } +// SendMsg sends a message through the gRPC client and returns the response. +// It wraps any encountered error for better error handling and context understanding. func (m *MessageRpcClient) SendMsg(ctx context.Context, req *msg.SendMsgReq) (*msg.SendMsgResp, error) { resp, err := m.Client.SendMsg(ctx, req) - return resp, err + if err != nil { + return nil, err + } + return resp, nil } +// GetMaxSeq retrieves the maximum sequence number from the gRPC client. +// Errors during the gRPC call are wrapped to provide additional context. func (m *MessageRpcClient) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sdkws.GetMaxSeqResp, error) { resp, err := m.Client.GetMaxSeq(ctx, req) - return resp, err + if err != nil { + return nil, err + } + return resp, nil } func (m *MessageRpcClient) GetMaxSeqs(ctx context.Context, conversationIDs []string) (map[string]int64, error) { @@ -181,9 +191,15 @@ func (m *MessageRpcClient) GetMsgByConversationIDs(ctx context.Context, docIDs [ return resp.MsgDatas, err } +// PullMessageBySeqList retrieves messages by their sequence numbers using the gRPC client. +// It directly forwards the request to the gRPC client and returns the response along with any error encountered. func (m *MessageRpcClient) PullMessageBySeqList(ctx context.Context, req *sdkws.PullMessageBySeqsReq) (*sdkws.PullMessageBySeqsResp, error) { resp, err := m.Client.PullMessageBySeqs(ctx, req) - return resp, err + if err != nil { + // Wrap the error to provide more context if the gRPC call fails. + return nil, err + } + return resp, nil } func (m *MessageRpcClient) GetConversationMaxSeq(ctx context.Context, conversationID string) (int64, error) { diff --git a/pkg/rpcclient/notification/conversation.go b/pkg/rpcclient/notification/conversation.go index 0fefb147e..3bbad746e 100644 --- a/pkg/rpcclient/notification/conversation.go +++ b/pkg/rpcclient/notification/conversation.go @@ -31,7 +31,7 @@ func NewConversationNotificationSender(msgRpcClient *rpcclient.MessageRpcClient) return &ConversationNotificationSender{rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient))} } -// SetPrivate调用. +// SetPrivate invote. func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx context.Context, sendID, recvID string, isPrivateChat bool, conversationID string, ) error { @@ -45,7 +45,6 @@ func (c *ConversationNotificationSender) ConversationSetPrivateNotification(ctx return c.Notification(ctx, sendID, recvID, constant.ConversationPrivateChatNotification, tips) } -// 会话改变. func (c *ConversationNotificationSender) ConversationChangeNotification(ctx context.Context, userID string, conversationIDs []string) error { tips := &sdkws.ConversationUpdateTips{ UserID: userID, @@ -55,7 +54,6 @@ func (c *ConversationNotificationSender) ConversationChangeNotification(ctx cont return c.Notification(ctx, userID, userID, constant.ConversationChangeNotification, tips) } -// 会话未读数同步. func (c *ConversationNotificationSender) ConversationUnreadChangeNotification( ctx context.Context, userID, conversationID string, diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index a55bc0e5e..a6b693724 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -31,7 +31,7 @@ import ( type FriendNotificationSender struct { *rpcclient.NotificationSender - // 找不到报错 + // Target not found err getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error) // db controller db controller.FriendDatabase diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index 6d0876972..b5a5c49cf 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -23,6 +23,7 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type Push struct { @@ -34,7 +35,7 @@ type Push struct { func NewPush(discov discoveryregistry.SvcDiscoveryRegistry) *Push { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImPushName) if err != nil { - panic(err) + util.ExitWithError(err) } return &Push{ discov: discov, @@ -49,9 +50,6 @@ func NewPushRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) PushRpcClie return PushRpcClient(*NewPush(discov)) } -func (p *PushRpcClient) DelUserPushToken( - ctx context.Context, - req *push.DelUserPushTokenReq, -) (*push.DelUserPushTokenResp, error) { +func (p *PushRpcClient) DelUserPushToken(ctx context.Context, req *push.DelUserPushTokenReq) (*push.DelUserPushTokenResp, error) { return p.Client.DelUserPushToken(ctx, req) } diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index b3557bf83..05b825060 100755 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -24,8 +24,10 @@ import ( "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type Third struct { @@ -38,35 +40,42 @@ type Third struct { func NewThird(discov discoveryregistry.SvcDiscoveryRegistry) *Third { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImThirdName) if err != nil { - panic(err) + util.ExitWithError(err) } client := third.NewThirdClient(conn) minioClient, err := minioInit() if err != nil { - panic(err) + util.ExitWithError(err) } return &Third{discov: discov, Client: client, conn: conn, MinioClient: minioClient} } func minioInit() (*minio.Client, error) { - minioClient := &minio.Client{} - initUrl := config.Config.Object.Minio.Endpoint - minioUrl, err := url.Parse(initUrl) + // Retrieve MinIO configuration details + endpoint := config.Config.Object.Minio.Endpoint + accessKeyID := config.Config.Object.Minio.AccessKeyID + secretAccessKey := config.Config.Object.Minio.SecretAccessKey + + // Parse the MinIO URL to determine if the connection should be secure + minioURL, err := url.Parse(endpoint) if err != nil { - return nil, err + return nil, errs.Wrap(err, "minioInit: failed to parse MinIO endpoint URL") } + + // Determine the security of the connection based on the scheme + secure := minioURL.Scheme == "https" + + // Setup MinIO client options opts := &minio.Options{ - Creds: credentials.NewStaticV4(config.Config.Object.Minio.AccessKeyID, config.Config.Object.Minio.SecretAccessKey, ""), - // Region: config.Config.Credential.Minio.Location, + Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), + Secure: secure, } - if minioUrl.Scheme == "http" { - opts.Secure = false - } else if minioUrl.Scheme == "https" { - opts.Secure = true - } - minioClient, err = minio.New(minioUrl.Host, opts) + + // Initialize MinIO client + minioClient, err := minio.New(minioURL.Host, opts) if err != nil { - return nil, err + return nil, errs.Wrap(err, "minioInit: failed to create MinIO client") } + return minioClient, nil } diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 451914cd3..810a88ad0 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -19,6 +19,7 @@ import ( "strings" "github.com/openimsdk/open-im-server/v3/pkg/authverify" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "google.golang.org/grpc" @@ -42,7 +43,7 @@ type User struct { func NewUser(discov discoveryregistry.SvcDiscoveryRegistry) *User { conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImUserName) if err != nil { - panic(err) + util.ExitWithError(err) } client := user.NewUserClient(conn) return &User{Discov: discov, Client: client, conn: conn} diff --git a/pkg/util/genutil/genutil.go b/pkg/util/genutil/genutil.go index bd9376d58..95735485d 100644 --- a/pkg/util/genutil/genutil.go +++ b/pkg/util/genutil/genutil.go @@ -18,6 +18,8 @@ import ( "fmt" "os" "path/filepath" + + "github.com/OpenIMSDK/tools/errs" ) // OutDir creates the absolute path name from path and checks path exists. @@ -25,16 +27,16 @@ import ( func OutDir(path string) (string, error) { outDir, err := filepath.Abs(path) if err != nil { - return "", err + return "", errs.Wrap(err, "output directory %s does not exist", path) } stat, err := os.Stat(outDir) if err != nil { - return "", err + return "", errs.Wrap(err, "output directory %s does not exist", outDir) } if !stat.IsDir() { - return "", fmt.Errorf("output directory %s is not a directory", outDir) + return "", errs.Wrap(err, "output directory %s is not a directory", outDir) } outDir += "/" return outDir, nil diff --git a/scripts/install/openim-tools.sh b/scripts/install/openim-tools.sh index 72005ed8e..d8c27e985 100755 --- a/scripts/install/openim-tools.sh +++ b/scripts/install/openim-tools.sh @@ -103,14 +103,14 @@ function openim::tools::start_service() { printf "Specifying prometheus port: %s\n" "${prometheus_port}" cmd="${cmd} --prometheus_port ${prometheus_port}" fi - openim::log::status "Starting ${binary_name}..." + openim::log::status "Starting binary ${binary_name}..." ${cmd} | tee -a "${LOG_FILE}" } function openim::tools::start() { openim::log::info "Starting OpenIM Tools..." for tool in "${OPENIM_TOOLS_NAME_LISTARIES[@]}"; do - openim::log::info "Starting ${tool}..." + openim::log::info "Starting tool ${tool}..." # openim::tools::start_service ${tool} sleep 0.2 done @@ -120,7 +120,7 @@ function openim::tools::start() { function openim::tools::pre-start() { openim::log::info "Preparing to start OpenIM Tools..." for tool in "${OPENIM_TOOLS_PRE_START_NAME_LISTARIES[@]}"; do - openim::log::info "Starting ${tool}..." + openim::log::info "Starting tool ${tool}..." openim::tools::start_service ${tool} ${OPNEIM_CONFIG} done } @@ -128,7 +128,7 @@ function openim::tools::pre-start() { function openim::tools::post-start() { openim::log::info "Post-start actions for OpenIM Tools..." for tool in "${OPENIM_TOOLS_POST_START_NAME_LISTARIES[@]}"; do - openim::log::info "Starting ${tool}..." + openim::log::info "Starting tool ${tool}..." openim::tools::start_service ${tool} done } diff --git a/scripts/make-rules/golang.mk b/scripts/make-rules/golang.mk index 915639b61..5a5d1a788 100644 --- a/scripts/make-rules/golang.mk +++ b/scripts/make-rules/golang.mk @@ -17,7 +17,7 @@ # GO := go -GO_SUPPORTED_VERSIONS ?= 1.19|1.20|1.21|1.22 +GO_SUPPORTED_VERSIONS ?= 1.19|1.20|1.21|1.22|1.23 GO_LDFLAGS += -X $(VERSION_PACKAGE).gitVersion=$(GIT_TAG) \ -X $(VERSION_PACKAGE).gitCommit=$(GIT_COMMIT) \ diff --git a/test/codescan/main.go b/test/codescan/main.go new file mode 100644 index 000000000..06ab7d0f9 --- /dev/null +++ b/test/codescan/main.go @@ -0,0 +1 @@ +package main diff --git a/test/e2e/README.md b/test/e2e/README.md index 1e73dbaec..49215e811 100644 --- a/test/e2e/README.md +++ b/test/e2e/README.md @@ -128,6 +128,8 @@ Open issue: https://github.com/openimsdk/open-im-server/issues/new/choose, choos The E2E test suite is integrated with CI, which runs the tests automatically on each code commit. The results are reported back to the pull request or commit to provide immediate feedback on the impact of the changes. +[![OpenIM Linux System E2E Test](https://github.com/openimsdk/open-im-server/actions/workflows/e2e-test.yml/badge.svg)](https://github.com/openimsdk/open-im-server/actions/workflows/e2e-test.yml) + ## Contact diff --git a/test/e2e/api/token/token.go b/test/e2e/api/token/token.go index 88af72058..b4dbda330 100644 --- a/test/e2e/api/token/token.go +++ b/test/e2e/api/token/token.go @@ -19,7 +19,6 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" ) @@ -58,20 +57,6 @@ type UserRegisterRequest struct { Users []User `json:"users"` } -func main() { - // Example usage of functions - token, err := GetUserToken("openIM123456") - if err != nil { - log.Fatalf("Error getting user token: %v", err) - } - fmt.Println("Token:", token) - - err = RegisterUser(token, "testUserID", "TestNickname", "https://example.com/image.jpg") - if err != nil { - log.Fatalf("Error registering user: %v", err) - } -} - // GetUserToken requests a user token from the API. func GetUserToken(userID string) (string, error) { reqBody := UserTokenRequest{ diff --git a/test/e2e/framework/config/config_test.go b/test/e2e/framework/config/config_test.go index b7259bf37..66f845db3 100644 --- a/test/e2e/framework/config/config_test.go +++ b/test/e2e/framework/config/config_test.go @@ -76,7 +76,7 @@ func TestCopyFlags(t *testing.T) { }() CopyFlags(tt.args.source, tt.args.target) - // 验证复制的标记 + // Verify the replicated tag if !tt.wantErr { tt.args.source.VisitAll(func(f *flag.Flag) { if gotFlag := tt.args.target.Lookup(f.Name); gotFlag == nil || !reflect.DeepEqual(gotFlag, f) { diff --git a/test/e2e/framework/helpers/chat/chat.go b/test/e2e/framework/helpers/chat/chat.go index a4ead528b..82dcc1aab 100644 --- a/test/e2e/framework/helpers/chat/chat.go +++ b/test/e2e/framework/helpers/chat/chat.go @@ -38,6 +38,9 @@ func main() { // } latestVersion := defaultTemplateVersion + // getLatestVersion + // getLatestVersion + // Construct the download URL downloadURL := fmt.Sprintf("https://github.com/openimsdk/chat/releases/download/%s/chat_Linux_x86_64.tar.gz", latestVersion) @@ -98,23 +101,23 @@ func main() { select {} } -// getLatestVersion fetches the latest version number from a given URL. -func getLatestVersion(url string) (string, error) { - resp, err := http.Get(url) - if err != nil { - return "", err - } - defer resp.Body.Close() - - location := resp.Header.Get("Location") - if location == "" { - return defaultTemplateVersion, nil - } - - // Extract the version number from the URL - latestVersion := filepath.Base(location) - return latestVersion, nil -} +// // getLatestVersion fetches the latest version number from a given URL. +// func getLatestVersion(url string) (string, error) { +// resp, err := http.Get(url) +// if err != nil { +// return "", err +// } +// defer resp.Body.Close() + +// location := resp.Header.Get("Location") +// if location == "" { +// return defaultTemplateVersion, nil +// } + +// // Extract the version number from the URL +// latestVersion := filepath.Base(location) +// return latestVersion, nil +// } // downloadAndExtract downloads a file from a URL and extracts it to a destination directory. func downloadAndExtract(url, destDir string) error { diff --git a/test/e2e/page/chat_page.go b/test/e2e/page/chat_page.go new file mode 100644 index 000000000..52aa8377d --- /dev/null +++ b/test/e2e/page/chat_page.go @@ -0,0 +1 @@ +package page diff --git a/test/e2e/page/login_page.go b/test/e2e/page/login_page.go new file mode 100644 index 000000000..52aa8377d --- /dev/null +++ b/test/e2e/page/login_page.go @@ -0,0 +1 @@ +package page diff --git a/test/e2e/web/.keep b/test/e2e/web/.keep deleted file mode 100644 index 4f07f1caf..000000000 --- a/test/e2e/web/.keep +++ /dev/null @@ -1 +0,0 @@ -.keep \ No newline at end of file diff --git a/test/e2e/web/Readme.md b/test/e2e/web/Readme.md new file mode 100644 index 000000000..741ca51d5 --- /dev/null +++ b/test/e2e/web/Readme.md @@ -0,0 +1,2 @@ +# OpenIM Web E2E + diff --git a/tools/data-conversion/chat/cmd/conversion-chat/chat.go b/tools/data-conversion/chat/cmd/conversion-chat/chat.go index 0fc49c782..f68b71b16 100644 --- a/tools/data-conversion/chat/cmd/conversion-chat/chat.go +++ b/tools/data-conversion/chat/cmd/conversion-chat/chat.go @@ -28,20 +28,20 @@ import ( func main() { var ( - usernameV2 = "root" // v2版本mysql用户名 - passwordV2 = "openIM" // v2版本mysql密码 - addrV2 = "127.0.0.1:13306" // v2版本mysql地址 - databaseV2 = "admin_chat" // v2版本mysql数据库名字 + usernameV2 = "root" // Username for MySQL v2 version + passwordV2 = "openIM" // Password for MySQL v2 version + addrV2 = "127.0.0.1:13306" // Address for MySQL v2 version + databaseV2 = "admin_chat" // Database name for MySQL v2 version ) var ( - usernameV3 = "root" // v3版本mysql用户名 - passwordV3 = "openIM123" // v3版本mysql密码 - addrV3 = "127.0.0.1:13306" // v3版本mysql地址 - databaseV3 = "openim_enterprise" // v3版本mysql数据库名字 + usernameV3 = "root" // Username for MySQL v3 version + passwordV3 = "openIM123" // Password for MySQL v3 version + addrV3 = "127.0.0.1:13306" // Address for MySQL v3 version + databaseV3 = "openim_enterprise" // Database name for MySQL v3 version ) - var concurrency = 1 // 并发数量 + var concurrency = 1 // Concurrency quantity log.SetFlags(log.LstdFlags | log.Llongfile) dsnV2 := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", usernameV2, passwordV2, addrV2, databaseV2) diff --git a/tools/data-conversion/chat/v2/admin.go b/tools/data-conversion/chat/v2/admin.go index fec11ff5b..20cf22a36 100644 --- a/tools/data-conversion/chat/v2/admin.go +++ b/tools/data-conversion/chat/v2/admin.go @@ -18,7 +18,7 @@ import ( "time" ) -// AppVersion pc端版本管理 +// AppVersion manages PC client versions type AppVersion struct { Version string `gorm:"column:version;size:64" json:"version"` Type int `gorm:"column:type;primary_key" json:"type"` @@ -29,7 +29,7 @@ type AppVersion struct { UpdateLog string `gorm:"column:update_log" json:"update_log"` } -// Admin 后台管理员 +// Admin manages backend administrators type Admin struct { Account string `gorm:"column:account;primary_key;type:char(64)" json:"account"` Password string `gorm:"column:Password;type:char(64)" json:"password"` @@ -40,19 +40,19 @@ type Admin struct { CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// RegisterAddFriend 注册时默认好友 +// RegisterAddFriend specifies default friends when registering type RegisterAddFriend struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// RegisterAddGroup 注册时默认群组 +// RegisterAddGroup specifies default groups when registering type RegisterAddGroup struct { GroupID string `gorm:"column:group_id;primary_key;type:char(64)" json:"userID"` CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// ClientInitConfig 系统相关配置项 +// ClientInitConfig contains system-related configuration items type ClientInitConfig struct { DiscoverPageURL string `gorm:"column:discover_page_url;size:128" json:"discoverPageURL"` OrdinaryUserAddFriend int32 `gorm:"column:ordinary_user_add_friend; default:1" json:"ordinaryUserAddFriend"` diff --git a/tools/data-conversion/chat/v2/chat.go b/tools/data-conversion/chat/v2/chat.go index 15cc4797f..4e0a0c04a 100644 --- a/tools/data-conversion/chat/v2/chat.go +++ b/tools/data-conversion/chat/v2/chat.go @@ -18,7 +18,7 @@ import ( "time" ) -// Register 注册信息表 +// Register Registration information sheet type Register struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` DeviceID string `gorm:"column:device_id;type:varchar(255)" json:"deviceID"` @@ -29,7 +29,7 @@ type Register struct { CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// Account 账号密码表 +// Account username and password table type Account struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` Password string `gorm:"column:password;type:varchar(255)" json:"password"` @@ -38,7 +38,7 @@ type Account struct { OperatorUserID string `gorm:"column:operator_user_id;type:varchar(64)" json:"operatorUserID"` } -// Attribute 用户属性表 +// Attribute user information table type Attribute struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` Account string `gorm:"column:account;type:char(64)" json:"account"` @@ -58,7 +58,7 @@ type Attribute struct { AllowAddFriend int32 `gorm:"column:allow_add_friend;default:1" json:"allowAddFriend"` } -// 封号表 +// User friend relationship table type ForbiddenAccount struct { UserID string `gorm:"column:user_id;index:userID;primary_key;type:char(64)" json:"userID"` CreateTime time.Time `gorm:"column:create_time" json:"createTime"` @@ -66,7 +66,7 @@ type ForbiddenAccount struct { OperatorUserID string `gorm:"column:operator_user_id;type:varchar(255)" json:"operatorUserID"` } -// 用户登录信息表 +// user login record table type UserLoginRecord struct { UserID string `gorm:"column:user_id;size:64" json:"userID"` LoginTime time.Time `gorm:"column:login_time" json:"loginTime"` @@ -75,7 +75,7 @@ type UserLoginRecord struct { Platform string `gorm:"column:platform;type:varchar(32)" json:"platform"` } -// 禁止ip登录 注册 +// ip login registration is prohibited type IPForbidden struct { IP string `gorm:"column:ip;primary_key;type:char(32)" json:"ip"` LimitRegister int32 `gorm:"column:limit_register" json:"limitRegister"` @@ -83,14 +83,14 @@ type IPForbidden struct { CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// 限制userID只能在某些ip登录 +// Restrict userids to certain ip addresses type LimitUserLoginIP struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)" json:"userID"` IP string `gorm:"column:ip;primary_key;type:char(32)" json:"ip"` CreateTime time.Time `gorm:"column:create_time" json:"createTime"` } -// 邀请码被注册使用 +// The invitation code is registered for use type InvitationRegister struct { InvitationCode string `gorm:"column:invitation_code;primary_key;type:char(32)" json:"invitationCode"` CreateTime time.Time `gorm:"column:create_time" json:"createTime"` diff --git a/tools/data-conversion/chat/v3/admin/admin.go b/tools/data-conversion/chat/v3/admin/admin.go index 22a81a068..90bd7f8f7 100644 --- a/tools/data-conversion/chat/v3/admin/admin.go +++ b/tools/data-conversion/chat/v3/admin/admin.go @@ -18,7 +18,7 @@ import ( "time" ) -// Admin 后台管理员. +// Admin Background administrator. type Admin struct { Account string `gorm:"column:account;primary_key;type:varchar(64)"` Password string `gorm:"column:password;type:varchar(64)"` diff --git a/tools/data-conversion/chat/v3/admin/client_config.go b/tools/data-conversion/chat/v3/admin/client_config.go index ceccd5105..48869fceb 100644 --- a/tools/data-conversion/chat/v3/admin/client_config.go +++ b/tools/data-conversion/chat/v3/admin/client_config.go @@ -14,7 +14,7 @@ package admin -// ClientConfig 客户端相关配置项. +// ClientConfig Client related configuration items. type ClientConfig struct { Key string `gorm:"column:key;primary_key;type:varchar(255)"` Value string `gorm:"column:value;not null;type:text"` diff --git a/tools/data-conversion/chat/v3/admin/forbidden_account.go b/tools/data-conversion/chat/v3/admin/forbidden_account.go index 104e793b0..e08125699 100644 --- a/tools/data-conversion/chat/v3/admin/forbidden_account.go +++ b/tools/data-conversion/chat/v3/admin/forbidden_account.go @@ -18,7 +18,7 @@ import ( "time" ) -// ForbiddenAccount 封号表. +// ForbiddenAccount forbidden account. type ForbiddenAccount struct { UserID string `gorm:"column:user_id;index:userID;primary_key;type:char(64)"` Reason string `gorm:"column:reason;type:varchar(255)" ` diff --git a/tools/data-conversion/chat/v3/admin/invitation_register.go b/tools/data-conversion/chat/v3/admin/invitation_register.go index 60f9067e2..4b71ccfe0 100644 --- a/tools/data-conversion/chat/v3/admin/invitation_register.go +++ b/tools/data-conversion/chat/v3/admin/invitation_register.go @@ -18,7 +18,7 @@ import ( "time" ) -// 邀请码被注册使用. +// The invitation code is registered for use. type InvitationRegister struct { InvitationCode string `gorm:"column:invitation_code;primary_key;type:char(32)"` UsedByUserID string `gorm:"column:user_id;index:userID;type:char(64)"` diff --git a/tools/data-conversion/chat/v3/admin/ip_forbidden.go b/tools/data-conversion/chat/v3/admin/ip_forbidden.go index 40c9257ef..886924abb 100644 --- a/tools/data-conversion/chat/v3/admin/ip_forbidden.go +++ b/tools/data-conversion/chat/v3/admin/ip_forbidden.go @@ -18,7 +18,7 @@ import ( "time" ) -// 禁止ip登录 注册. +// ip login registration is prohibited. type IPForbidden struct { IP string `gorm:"column:ip;primary_key;type:char(32)"` LimitRegister bool `gorm:"column:limit_register"` diff --git a/tools/data-conversion/chat/v3/admin/limit_user_login_ip.go b/tools/data-conversion/chat/v3/admin/limit_user_login_ip.go index 8427eaf80..0eaa5bc1e 100644 --- a/tools/data-conversion/chat/v3/admin/limit_user_login_ip.go +++ b/tools/data-conversion/chat/v3/admin/limit_user_login_ip.go @@ -18,7 +18,7 @@ import ( "time" ) -// 限制userID只能在某些ip登录. +// Restrict userids to certain ip addresses. type LimitUserLoginIP struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)"` IP string `gorm:"column:ip;primary_key;type:char(32)"` diff --git a/tools/data-conversion/chat/v3/admin/register_add_friend.go b/tools/data-conversion/chat/v3/admin/register_add_friend.go index e21896d90..8281f6485 100644 --- a/tools/data-conversion/chat/v3/admin/register_add_friend.go +++ b/tools/data-conversion/chat/v3/admin/register_add_friend.go @@ -18,7 +18,7 @@ import ( "time" ) -// RegisterAddFriend 注册时默认好友. +// RegisterAddFriend Indicates the default friend when registering. type RegisterAddFriend struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)"` CreateTime time.Time `gorm:"column:create_time"` diff --git a/tools/data-conversion/chat/v3/admin/register_add_group.go b/tools/data-conversion/chat/v3/admin/register_add_group.go index e9c1317b9..1204ff97b 100644 --- a/tools/data-conversion/chat/v3/admin/register_add_group.go +++ b/tools/data-conversion/chat/v3/admin/register_add_group.go @@ -18,7 +18,7 @@ import ( "time" ) -// RegisterAddGroup 注册时默认群组. +// RegisterAddGroup Indicates the default group for registration. type RegisterAddGroup struct { GroupID string `gorm:"column:group_id;primary_key;type:char(64)"` CreateTime time.Time `gorm:"column:create_time"` diff --git a/tools/data-conversion/chat/v3/chat/account.go b/tools/data-conversion/chat/v3/chat/account.go index d2117e7ca..6d01c20e2 100644 --- a/tools/data-conversion/chat/v3/chat/account.go +++ b/tools/data-conversion/chat/v3/chat/account.go @@ -18,7 +18,7 @@ import ( "time" ) -// Account 账号密码表. +// Account Account password table. type Account struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)"` Password string `gorm:"column:password;type:varchar(32)"` diff --git a/tools/data-conversion/chat/v3/chat/attribute.go b/tools/data-conversion/chat/v3/chat/attribute.go index 6a6f975d1..23de217bd 100644 --- a/tools/data-conversion/chat/v3/chat/attribute.go +++ b/tools/data-conversion/chat/v3/chat/attribute.go @@ -18,7 +18,7 @@ import ( "time" ) -// Attribute 用户属性表. +// Attribute Indicates the user attribute table. type Attribute struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)"` Account string `gorm:"column:account;type:char(64)"` diff --git a/tools/data-conversion/chat/v3/chat/register.go b/tools/data-conversion/chat/v3/chat/register.go index 740159436..29e5cb698 100644 --- a/tools/data-conversion/chat/v3/chat/register.go +++ b/tools/data-conversion/chat/v3/chat/register.go @@ -18,7 +18,7 @@ import ( "time" ) -// Register 注册信息表. +// Register Indicates the registration information. type Register struct { UserID string `gorm:"column:user_id;primary_key;type:char(64)"` DeviceID string `gorm:"column:device_id;type:varchar(255)"` diff --git a/tools/data-conversion/chat/v3/chat/user_login_record.go b/tools/data-conversion/chat/v3/chat/user_login_record.go index 8db3699d6..31e57b6ce 100644 --- a/tools/data-conversion/chat/v3/chat/user_login_record.go +++ b/tools/data-conversion/chat/v3/chat/user_login_record.go @@ -18,7 +18,7 @@ import ( "time" ) -// 用户登录信息表. +// User login information table. type UserLoginRecord struct { UserID string `gorm:"column:user_id;size:64"` LoginTime time.Time `gorm:"column:login_time"` diff --git a/tools/data-conversion/openim/cmd/conversion-msg/conversion-msg.go b/tools/data-conversion/openim/cmd/conversion-msg/conversion-msg.go index f2b9623a6..416fdcb9f 100644 --- a/tools/data-conversion/openim/cmd/conversion-msg/conversion-msg.go +++ b/tools/data-conversion/openim/cmd/conversion-msg/conversion-msg.go @@ -38,11 +38,20 @@ import ( func main() { var ( - topic = "ws2ms_chat" // v2版本配置文件kafka.topic.ws2ms_chat - kafkaAddr = "127.0.0.1:9092" // v2版本配置文件kafka.topic.addr - rpcAddr = "127.0.0.1:10130" // v3版本配置文件rpcPort.openImMessagePort - adminUserID = "openIM123456" // v3版本管理员userID - concurrency = 1 // 并发数量 + // The Kafka topic for ws2ms_chat in version 2 configuration + topic = "ws2ms_chat" + + // The Kafka address in version 2 configuration + kafkaAddr = "127.0.0.1:9092" + + // The RPC address in version 3 configuration + rpcAddr = "127.0.0.1:10130" + + // The administrator userID in version 3 + adminUserID = "openIM123456" + + // The number of concurrent processes + concurrency = 1 ) getRpcConn := func() (*grpc.ClientConn, error) { @@ -99,7 +108,7 @@ func main() { ch := pc.Messages() for { select { - case <-time.After(time.Second * 10): // 10s读取不到就关闭 + case <-time.After(time.Second * 10): // 10s Shuts down when the data cannot be read return case message, ok := <-ch: if !ok { diff --git a/tools/data-conversion/openim/cmd/conversion-mysql/conversion-mysql.go b/tools/data-conversion/openim/cmd/conversion-mysql/conversion-mysql.go index 8a951e16f..08fa4ca55 100644 --- a/tools/data-conversion/openim/cmd/conversion-mysql/conversion-mysql.go +++ b/tools/data-conversion/openim/cmd/conversion-mysql/conversion-mysql.go @@ -27,21 +27,37 @@ import ( ) func main() { + var ( - usernameV2 = "root" // v2版本mysql用户名 - passwordV2 = "openIM" // v2版本mysql密码 - addrV2 = "127.0.0.1:13306" // v2版本mysql地址 - databaseV2 = "openIM_v2" // v2版本mysql数据库名字 + // MySQL username for version 2 + usernameV2 = "root" + + // MySQL password for version 2 + passwordV2 = "openIM" + + // MySQL address for version 2 + addrV2 = "127.0.0.1:13306" + + // MySQL database name for version 2 + databaseV2 = "openIM_v2" ) var ( - usernameV3 = "root" // v3版本mysql用户名 - passwordV3 = "openIM123" // v3版本mysql密码 - addrV3 = "127.0.0.1:13306" // v3版本mysql地址 - databaseV3 = "openim_v3" // v3版本mysql数据库名字 + // MySQL username for version 3 + usernameV3 = "root" + + // MySQL password for version 3 + passwordV3 = "openIM123" + + // MySQL address for version 3 + addrV3 = "127.0.0.1:13306" + + // MySQL database name for version 3 + databaseV3 = "openim_v3" ) - var concurrency = 1 // 并发数量 + // The number of concurrent processes + var concurrency = 1 log.SetFlags(log.LstdFlags | log.Llongfile) dsnV2 := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", usernameV2, passwordV2, addrV2, databaseV2) diff --git a/tools/data-conversion/openim/mysql/v3/friend.go b/tools/data-conversion/openim/mysql/v3/friend.go index 58d8d1d34..4f3fb6bdf 100644 --- a/tools/data-conversion/openim/mysql/v3/friend.go +++ b/tools/data-conversion/openim/mysql/v3/friend.go @@ -38,41 +38,30 @@ func (FriendModel) TableName() string { } type FriendModelInterface interface { - // 插入多条记录 - Create(ctx context.Context, friends []*FriendModel) (err error) - // 删除ownerUserID指定的好友 - Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) (err error) - // 更新ownerUserID单个好友信息 更新零值 - UpdateByMap(ctx context.Context, ownerUserID string, friendUserID string, args map[string]interface{}) (err error) - // 更新好友信息的非零值 - Update(ctx context.Context, friends []*FriendModel) (err error) - // 更新好友备注(也支持零值 ) - UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) (err error) - // 获取单个好友信息,如没找到 返回错误 - Take(ctx context.Context, ownerUserID, friendUserID string) (friend *FriendModel, err error) - // 查找好友关系,如果是双向关系,则都返回 - FindUserState(ctx context.Context, userID1, userID2 string) (friends []*FriendModel, err error) - // 获取 owner指定的好友列表 如果有friendUserIDs不存在,也不返回错误 - FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) (friends []*FriendModel, err error) - // 获取哪些人添加了friendUserID 如果有ownerUserIDs不存在,也不返回错误 - FindReversalFriends( - ctx context.Context, - friendUserID string, - ownerUserIDs []string, - ) (friends []*FriendModel, err error) - // 获取ownerUserID好友列表 支持翻页 - FindOwnerFriends( - ctx context.Context, - ownerUserID string, - pageNumber, showNumber int32, - ) (friends []*FriendModel, total int64, err error) - // 获取哪些人添加了friendUserID 支持翻页 - FindInWhoseFriends( - ctx context.Context, - friendUserID string, - pageNumber, showNumber int32, - ) (friends []*FriendModel, total int64, err error) - // 获取好友UserID列表 - FindFriendUserIDs(ctx context.Context, ownerUserID string) (friendUserIDs []string, err error) + // Create inserts multiple friend records. + Create(ctx context.Context, friends []*FriendModel) error + // Delete removes specified friends for an owner user. + Delete(ctx context.Context, ownerUserID string, friendUserIDs []string) error + // UpdateByMap updates a single friend's information for an owner user based on a map of arguments. Zero values are updated. + UpdateByMap(ctx context.Context, ownerUserID string, friendUserID string, args map[string]interface{}) error + // Update modifies the information of friends, excluding zero values. + Update(ctx context.Context, friends []*FriendModel) error + // UpdateRemark updates the remark for a friend, supporting zero values. + UpdateRemark(ctx context.Context, ownerUserID, friendUserID, remark string) error + // Take retrieves a single friend's information. Returns an error if not found. + Take(ctx context.Context, ownerUserID, friendUserID string) (*FriendModel, error) + // FindUserState finds the friendship status between two users, returning both if a mutual friendship exists. + FindUserState(ctx context.Context, userID1, userID2 string) ([]*FriendModel, error) + // FindFriends retrieves a list of friends for an owner, not returning an error for non-existent friendUserIDs. + FindFriends(ctx context.Context, ownerUserID string, friendUserIDs []string) ([]*FriendModel, error) + // FindReversalFriends finds who has added the specified user as a friend, not returning an error for non-existent ownerUserIDs. + FindReversalFriends(ctx context.Context, friendUserID string, ownerUserIDs []string) ([]*FriendModel, error) + // FindOwnerFriends paginates through the friends list of an owner user. + FindOwnerFriends(ctx context.Context, ownerUserID string, pageNumber, showNumber int32) ([]*FriendModel, int64, error) + // FindInWhoseFriends paginates through users who have added the specified user as a friend. + FindInWhoseFriends(ctx context.Context, friendUserID string, pageNumber, showNumber int32) ([]*FriendModel, int64, error) + // FindFriendUserIDs retrieves a list of friend user IDs for an owner user. + FindFriendUserIDs(ctx context.Context, ownerUserID string) ([]string, error) + // NewTx creates a new transactional instance of the FriendModelInterface. NewTx(tx any) FriendModelInterface } diff --git a/tools/data-conversion/openim/mysql/v3/friend_request.go b/tools/data-conversion/openim/mysql/v3/friend_request.go index 51ea0ef6e..4fc5ba7c1 100644 --- a/tools/data-conversion/openim/mysql/v3/friend_request.go +++ b/tools/data-conversion/openim/mysql/v3/friend_request.go @@ -38,29 +38,33 @@ func (FriendRequestModel) TableName() string { } type FriendRequestModelInterface interface { - // 插入多条记录 + // Insert multiple records Create(ctx context.Context, friendRequests []*FriendRequestModel) (err error) - // 删除记录 + + // Delete a record Delete(ctx context.Context, fromUserID, toUserID string) (err error) - // 更新零值 - UpdateByMap(ctx context.Context, formUserID string, toUserID string, args map[string]interface{}) (err error) - // 更新多条记录 (非零值) + + // Update records with zero values based on a map of changes + UpdateByMap(ctx context.Context, formUserID, toUserID string, args map[string]interface{}) (err error) + + // Update multiple records (non-zero values) Update(ctx context.Context, friendRequest *FriendRequestModel) (err error) - // 获取来指定用户的好友申请 未找到 不返回错误 + + // Find a friend request sent to a specific user; does not return an error if not found Find(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error) + + // Alias for Find (retrieves a friend request between two users) Take(ctx context.Context, fromUserID, toUserID string) (friendRequest *FriendRequestModel, err error) - // 获取toUserID收到的好友申请列表 - FindToUserID( - ctx context.Context, - toUserID string, - pageNumber, showNumber int32, - ) (friendRequests []*FriendRequestModel, total int64, err error) - // 获取fromUserID发出去的好友申请列表 - FindFromUserID( - ctx context.Context, - fromUserID string, - pageNumber, showNumber int32, - ) (friendRequests []*FriendRequestModel, total int64, err error) + + // Get a list of friend requests received by `toUserID` + FindToUserID(ctx context.Context, toUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error) + + // Get a list of friend requests sent by `fromUserID` + FindFromUserID(ctx context.Context, fromUserID string, pageNumber, showNumber int32) (friendRequests []*FriendRequestModel, total int64, err error) + + // Find all friend requests between two users (both directions) FindBothFriendRequests(ctx context.Context, fromUserID, toUserID string) (friends []*FriendRequestModel, err error) + + // Create a new transaction NewTx(tx any) FriendRequestModelInterface } diff --git a/tools/data-conversion/openim/mysql/v3/group.go b/tools/data-conversion/openim/mysql/v3/group.go index 6759e0d35..ccf61266a 100644 --- a/tools/data-conversion/openim/mysql/v3/group.go +++ b/tools/data-conversion/openim/mysql/v3/group.go @@ -58,9 +58,10 @@ type GroupModelInterface interface { keyword string, pageNumber, showNumber int32, ) (total uint32, groups []*GroupModel, err error) + // GetGroupIDsByCreatorUserID retrieves a list of group IDs created by the specified user. GetGroupIDsByGroupType(ctx context.Context, groupType int) (groupIDs []string, err error) - // 获取群总数 + // CountTotal retrieves the total number of groups. CountTotal(ctx context.Context, before *time.Time) (count int64, err error) - // 获取范围内群增量 + // CountRangeEverydayTotal retrieves the total number of groups created every day within the specified time range. CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } diff --git a/tools/data-conversion/openim/mysql/v3/user.go b/tools/data-conversion/openim/mysql/v3/user.go index 10a715bda..409689933 100644 --- a/tools/data-conversion/openim/mysql/v3/user.go +++ b/tools/data-conversion/openim/mysql/v3/user.go @@ -53,20 +53,35 @@ func (UserModel) TableName() string { return UserModelTableName } +// UserModelInterface defines the operations available for managing user models. type UserModelInterface interface { + // Create inserts a new user or multiple users into the database. Create(ctx context.Context, users []*UserModel) (err error) + + // UpdateByMap updates a user's information based on a map of changes. UpdateByMap(ctx context.Context, userID string, args map[string]interface{}) (err error) + + // Update modifies a user's information in the database. Update(ctx context.Context, user *UserModel) (err error) - // 获取指定用户信息 不存在,也不返回错误 + + // Find retrieves information for a list of users by their IDs. If a user does not exist, it is simply skipped without returning an error. Find(ctx context.Context, userIDs []string) (users []*UserModel, err error) - // 获取某个用户信息 不存在,则返回错误 + + // Take retrieves a specific user's information by their ID. Returns an error if the user does not exist. Take(ctx context.Context, userID string) (user *UserModel, err error) - // 获取用户信息 不存在,不返回错误 + + // Page retrieves a paginated list of users and the total count of users. If no users exist, returns an empty list without an error. Page(ctx context.Context, pageNumber, showNumber int32) (users []*UserModel, count int64, err error) + + // GetAllUserID retrieves all user IDs in a paginated manner. GetAllUserID(ctx context.Context, pageNumber, showNumber int32) (userIDs []string, err error) + + // GetUserGlobalRecvMsgOpt retrieves a user's global message receiving option. GetUserGlobalRecvMsgOpt(ctx context.Context, userID string) (opt int, err error) - // 获取用户总数 + + // CountTotal returns the total number of users before a specified time. CountTotal(ctx context.Context, before *time.Time) (count int64, err error) - // 获取范围内用户增量 + + // CountRangeEverydayTotal calculates the daily increment of users within a specified time range. CountRangeEverydayTotal(ctx context.Context, start time.Time, end time.Time) (map[string]int64, error) } diff --git a/tools/formitychecker/checker/checker.go b/tools/formitychecker/checker/checker.go index c9ad8239f..9a64b7090 100644 --- a/tools/formitychecker/checker/checker.go +++ b/tools/formitychecker/checker/checker.go @@ -21,6 +21,7 @@ import ( "regexp" "strings" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/tools/formitychecker/config" ) @@ -39,7 +40,7 @@ func CheckDirectory(cfg *config.Config) error { for _, targetDir := range cfg.TargetDirs { err := filepath.Walk(targetDir, func(path string, info os.FileInfo, err error) error { if err != nil { - return err + return errs.Wrap(err, fmt.Sprintf("error walking directory '%s'", targetDir)) } // Skip if the directory is in the ignore list diff --git a/tools/infra/infra.go b/tools/infra/infra.go index c14b92fa3..bc01a00eb 100644 --- a/tools/infra/infra.go +++ b/tools/infra/infra.go @@ -20,7 +20,7 @@ import ( "github.com/fatih/color" ) -// 定义一个函数以打印重要的链接信息 +// Define a function to print important link information func printLinks() { blue := color.New(color.FgBlue).SprintFunc() fmt.Printf("OpenIM Github: %s\n", blue("https://github.com/OpenIMSDK/Open-IM-Server")) @@ -47,5 +47,5 @@ Keep checking for updates! ` blue.Println(message) - printLinks() // 调用函数以打印链接信息 + printLinks() // Call the function to print the link information } diff --git a/tools/url2im/main.go b/tools/url2im/main.go index 8d6151b09..28c1e1c43 100644 --- a/tools/url2im/main.go +++ b/tools/url2im/main.go @@ -31,17 +31,38 @@ import ( */ func main() { - var conf pkg.Config // 后面带*的为必填项 - flag.StringVar(&conf.TaskPath, "task", "take.txt", "task path") // 任务日志文件* - flag.StringVar(&conf.ProgressPath, "progress", "", "progress path") // 进度日志文件 - flag.IntVar(&conf.Concurrency, "concurrency", 1, "concurrency num") // 并发数 - flag.IntVar(&conf.Retry, "retry", 1, "retry num") // 重试次数 - flag.StringVar(&conf.TempDir, "temp", "", "temp dir") // 临时文件夹 - flag.Int64Var(&conf.CacheSize, "cache", 1024*1024*100, "cache size") // 缓存大小(超过时,下载到磁盘) - flag.Int64Var((*int64)(&conf.Timeout), "timeout", 5000, "timeout") // 请求超时时间(毫秒) - flag.StringVar(&conf.Api, "api", "http://127.0.0.1:10002", "api") // im地址* - flag.StringVar(&conf.UserID, "userID", "openIM123456", "userID") // im管理员 - flag.StringVar(&conf.Secret, "secret", "openIM123", "secret") // im config secret + var conf pkg.Config // Configuration object, '*' denotes required fields + + // *Required*: Path for the task log file + flag.StringVar(&conf.TaskPath, "task", "take.txt", "Path for the task log file") + + // Optional: Path for the progress log file + flag.StringVar(&conf.ProgressPath, "progress", "", "Path for the progress log file") + + // Number of concurrent operations + flag.IntVar(&conf.Concurrency, "concurrency", 1, "Number of concurrent operations") + + // Number of retry attempts + flag.IntVar(&conf.Retry, "retry", 1, "Number of retry attempts") + + // Optional: Path for the temporary directory + flag.StringVar(&conf.TempDir, "temp", "", "Path for the temporary directory") + + // Cache size in bytes (downloads move to disk when exceeded) + flag.Int64Var(&conf.CacheSize, "cache", 1024*1024*100, "Cache size in bytes") + + // Request timeout in milliseconds + flag.Int64Var((*int64)(&conf.Timeout), "timeout", 5000, "Request timeout in milliseconds") + + // *Required*: API endpoint for the IM service + flag.StringVar(&conf.Api, "api", "http://127.0.0.1:10002", "API endpoint for the IM service") + + // IM administrator's user ID + flag.StringVar(&conf.UserID, "userID", "openIM123456", "IM administrator's user ID") + + // Secret for the IM configuration + flag.StringVar(&conf.Secret, "secret", "openIM123", "Secret for the IM configuration") + flag.Parse() if !filepath.IsAbs(conf.TaskPath) { var err error From 02a3cfb021f4b0adad41bbbe384a31d616bc9dce Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Mon, 4 Mar 2024 17:30:17 +0800 Subject: [PATCH 63/74] feat: Introduce Language-Specific Comment Detection Tool and Standardize Log Filename Convention (#1992) * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code * feat: optimize openim reset code --- .golangci.yml | 1 + config/templates/prometheus-dashboard.yaml | 8 +- docs/contrib/environment.md | 2 +- go.work | 1 + internal/api/user.go | 2 +- internal/msggateway/message_handler.go | 12 +-- internal/msgtransfer/init.go | 2 +- internal/rpc/group/group.go | 2 +- pkg/common/cmd/root.go | 4 +- scripts/build-all-service.sh | 3 - scripts/common.sh | 3 - scripts/install/environment.sh | 2 +- scripts/install/openim-rpc.sh | 4 - scripts/install/openim-tools.sh | 3 - scripts/lib/init.sh | 3 - scripts/lib/logging.sh | 6 +- scripts/lib/release.sh | 1 + scripts/verify-annotation-language.sh | 46 +++++++++++ scripts/verify-pkg-names.sh | 3 - test/codescan/main.go | 1 - tools/codescan/checker/checker.go | 90 ++++++++++++++++++++++ tools/codescan/codescan.go | 20 +++++ tools/codescan/config.yaml | 7 ++ tools/codescan/config/config.go | 35 +++++++++ tools/codescan/go.mod | 3 + 25 files changed, 224 insertions(+), 40 deletions(-) create mode 100755 scripts/verify-annotation-language.sh delete mode 100644 test/codescan/main.go create mode 100644 tools/codescan/checker/checker.go create mode 100644 tools/codescan/codescan.go create mode 100644 tools/codescan/config.yaml create mode 100644 tools/codescan/config/config.go create mode 100644 tools/codescan/go.mod diff --git a/.golangci.yml b/.golangci.yml index 0a0d40c21..67419d05e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -66,6 +66,7 @@ run: - "mocks/" - ".github/" - "logs/" + - "_output/" - "components/" # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": diff --git a/config/templates/prometheus-dashboard.yaml b/config/templates/prometheus-dashboard.yaml index 417f3d343..2e1ae7760 100644 --- a/config/templates/prometheus-dashboard.yaml +++ b/config/templates/prometheus-dashboard.yaml @@ -53,7 +53,7 @@ }, "id": 16, "panels": [], - "title": "openim自定义指标", + "title": "openim Custom Metrics", "type": "row" }, { @@ -144,7 +144,7 @@ "refId": "A" } ], - "title": "在线人数", + "title": "Online population", "type": "timeseries" }, { @@ -235,7 +235,7 @@ "refId": "A" } ], - "title": "登入/注册人数", + "title": "Login/registration numbers", "type": "timeseries" }, { @@ -1345,7 +1345,7 @@ "type": "timeseries" } ], - "title": "应用服务器流量指标", + "title": "Traffic indicators of the application server", "type": "row" } ], diff --git a/docs/contrib/environment.md b/docs/contrib/environment.md index dc11a3c7b..d2db7cbf3 100644 --- a/docs/contrib/environment.md +++ b/docs/contrib/environment.md @@ -449,7 +449,7 @@ This section involves configuring the log settings, including storage location, | Parameter | Example Value | Description | | ------------------------- | ------------------------ | --------------------------------- | -| LOG_STORAGE_LOCATION | "${OPENIM_ROOT}/logs/" | Location for storing logs | +| LOG_STORAGE_LOCATION | "${OPENIM_ROOT}/_output/logs/" | Location for storing logs | | LOG_ROTATION_TIME | "24" | Log rotation time (in hours) | | LOG_REMAIN_ROTATION_COUNT | "2" | Number of log rotations to retain | | LOG_REMAIN_LOG_LEVEL | "6" | Log level to retain | diff --git a/go.work b/go.work index 97d2816d6..be67ce842 100644 --- a/go.work +++ b/go.work @@ -3,6 +3,7 @@ go 1.19 use ( . ./test/typecheck + ./tools/codescan ./tools/changelog ./tools/component ./tools/data-conversion diff --git a/internal/api/user.go b/internal/api/user.go index 901998319..16efcd704 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -145,7 +145,7 @@ func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) { msgClient := msggateway.NewMsgGatewayClient(v) reply, err := msgClient.GetUsersOnlineStatus(c, &req) if err != nil { - log.ZWarn(c, "GetUsersOnlineStatus rpc err", err) + log.ZWarn(c, "GetUsersOnlineStatus rpc err", err) continue } else { wsResult = append(wsResult, reply.SuccessResult...) diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 3c5bd6121..74bde1ff5 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -183,7 +183,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([ return nil, errs.Wrap(err, "error unmarshaling request") } if err := g.validate.Struct(data); err != nil { - return nil, err + return nil, errs.Wrap(err, "validation failed") } resp, err := g.msgRpcClient.PullMessageBySeqList(context, &req) if err != nil { @@ -191,7 +191,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([ } c, err := proto.Marshal(resp) if err != nil { - return nil, err + return nil, errs.Wrap(err, "error marshaling response") } return c, nil } @@ -199,7 +199,7 @@ func (g GrpcHandler) PullMessageBySeqList(context context.Context, data *Req) ([ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, error) { req := push.DelUserPushTokenReq{} if err := proto.Unmarshal(data.Data, &req); err != nil { - return nil, err + return nil, errs.Wrap(err, "error unmarshaling request") } resp, err := g.pushClient.DelUserPushToken(context, &req) if err != nil { @@ -207,7 +207,7 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err } c, err := proto.Marshal(resp) if err != nil { - return nil, err + return nil, errs.Wrap(err, "error marshaling response") } return c, nil } @@ -215,10 +215,10 @@ func (g GrpcHandler) UserLogout(context context.Context, data *Req) ([]byte, err func (g GrpcHandler) SetUserDeviceBackground(_ context.Context, data *Req) ([]byte, bool, error) { req := sdkws.SetAppBackgroundStatusReq{} if err := proto.Unmarshal(data.Data, &req); err != nil { - return nil, false, err + return nil, false, errs.Wrap(err, "error unmarshaling request") } if err := g.validate.Struct(data); err != nil { - return nil, false, err + return nil, false, errs.Wrap(err, "validation failed") } return nil, req.IsBackground, nil } diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index b5f8516f8..ca9ab5ac4 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -121,7 +121,7 @@ func (m *MsgTransfer) Start(prometheusPort int) error { var ( netDone = make(chan struct{}, 1) - netErr error + netErr error ) go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH) diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 60f6c3eb5..db5108f31 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -974,7 +974,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf if len(update) == 0 { return resp, nil } - if updateErr := s.db.UpdateGroup(ctx, group.GroupID, update); updateErr != nil { + if err := s.db.UpdateGroup(ctx, group.GroupID, update); err != nil { return nil, err } group, err = s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID) diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 7256bd0ed..4f4c5e69b 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -47,7 +47,7 @@ type CmdOpts struct { func WithCronTaskLogName() func(*CmdOpts) { return func(opts *CmdOpts) { - opts.loggerPrefixName = "openim.crontask.log.all" + opts.loggerPrefixName = "openim-crontask" } } @@ -117,7 +117,7 @@ func (rc *RootCmd) initializeLogger(cmdOpts *CmdOpts) error { func defaultCmdOpts() *CmdOpts { return &CmdOpts{ - loggerPrefixName: "OpenIM.log.all", + loggerPrefixName: "openim-all", } } diff --git a/scripts/build-all-service.sh b/scripts/build-all-service.sh index 6335b0e08..eea380b4f 100755 --- a/scripts/build-all-service.sh +++ b/scripts/build-all-service.sh @@ -23,9 +23,6 @@ # Example: `scripts/build-go.sh WHAT=cmd/kubelet`. - - - OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/scripts/common.sh b/scripts/common.sh index 702f55588..f7155fca2 100755 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -19,9 +19,6 @@ # Common utilities, variables and checks for all build scripts. - - - # Unset CDPATH, having it set messes up with script import paths unset CDPATH diff --git a/scripts/install/environment.sh b/scripts/install/environment.sh index 896288775..64e76853d 100755 --- a/scripts/install/environment.sh +++ b/scripts/install/environment.sh @@ -332,7 +332,7 @@ def "OPENIM_CONVERSATION_NAME" "Conversation" # OpenIM对话服务名称 def "OPENIM_THIRD_NAME" "Third" # OpenIM第三方服务名称 ###################### Log Configuration Variables ###################### -def "LOG_STORAGE_LOCATION" "${OPENIM_ROOT}/logs/" # 日志存储位置 +def "LOG_STORAGE_LOCATION" "${OPENIM_ROOT}/_output/logs/" # 日志存储位置 def "LOG_ROTATION_TIME" "24" # 日志轮替时间 def "LOG_REMAIN_ROTATION_COUNT" "2" # 保留的日志轮替数量 def "LOG_REMAIN_LOG_LEVEL" "6" # 保留的日志级别 diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh index 023cb6594..e7ee430d2 100755 --- a/scripts/install/openim-rpc.sh +++ b/scripts/install/openim-rpc.sh @@ -38,10 +38,6 @@ # Note: Before executing this script, ensure that the necessary permissions are granted and relevant environmental variables are set. # - - - - OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh diff --git a/scripts/install/openim-tools.sh b/scripts/install/openim-tools.sh index d8c27e985..4eb722c6e 100755 --- a/scripts/install/openim-tools.sh +++ b/scripts/install/openim-tools.sh @@ -39,9 +39,6 @@ # - - - OPENIM_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")"/../.. && pwd -P) [[ -z ${COMMON_SOURCED} ]] && source "${OPENIM_ROOT}"/scripts/install/common.sh diff --git a/scripts/lib/init.sh b/scripts/lib/init.sh index 4cd6d9fb8..6f12db36e 100755 --- a/scripts/lib/init.sh +++ b/scripts/lib/init.sh @@ -14,9 +14,6 @@ # limitations under the License. - - - # Short-circuit if init.sh has already been sourced [[ $(type -t openim::init::loaded) == function ]] && return 0 diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index 7afb6bfce..bef3b5961 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -25,9 +25,9 @@ if [ -z "${OPENIM_OUTPUT+x}" ]; then fi # Set the log file path -LOG_FILE="${OPENIM_OUTPUT}/logs/openim_$(date '+%Y%m%d').log" -STDERR_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_error_$(date '+%Y%m%d').log" -TMP_LOG_FILE="${OPENIM_OUTPUT}/logs/openim_tmp_$(date '+%Y%m%d').log" +LOG_FILE="${OPENIM_OUTPUT}/logs/openim-$(date '+%Y%m%d').log" +STDERR_LOG_FILE="${OPENIM_OUTPUT}/logs/openim-error-$(date '+%Y%m%d').log" +TMP_LOG_FILE="${OPENIM_OUTPUT}/logs/openim-tmp-$(date '+%Y%m%d').log" if [[ ! -d "${OPENIM_OUTPUT}/logs" ]]; then mkdir -p "${OPENIM_OUTPUT}/logs" diff --git a/scripts/lib/release.sh b/scripts/lib/release.sh index 521e5cedc..c1fbd00a1 100755 --- a/scripts/lib/release.sh +++ b/scripts/lib/release.sh @@ -152,6 +152,7 @@ function openim::release::package_src_tarball() { -path "${OPENIM_ROOT}"/.github\* -o \ -path "${OPENIM_ROOT}"/components\* -o \ -path "${OPENIM_ROOT}"/logs\* -o \ + -path "${OPENIM_ROOT}"/_output\* -o \ -path "${OPENIM_ROOT}"/.gitignore\* -o \ -path "${OPENIM_ROOT}"/.gsemver.yml\* -o \ -path "${OPENIM_ROOT}"/.config\* -o \ diff --git a/scripts/verify-annotation-language.sh b/scripts/verify-annotation-language.sh new file mode 100755 index 000000000..6b863776c --- /dev/null +++ b/scripts/verify-annotation-language.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Copyright © 2023 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +# This script verifies whether codes follow golang convention. +# Usage: `scripts/verify-pkg-names.sh`. + +set -o errexit + +OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. +source "${OPENIM_ROOT}/scripts/lib/init.sh" + +openim::golang::verify_go_version + +openim::golang::verify_go_version + +OPENIM_OUTPUT_HOSTBIN_TOOLS="${OPENIM_ROOT}/_output/bin/tools/linux/amd64" +CODESCAN_BINARY="${OPENIM_OUTPUT_HOSTBIN_TOOLS}/codescan" + +if [[ ! -f "${CODESCAN_BINARY}" ]]; then + echo "codescan binary not found, building..." + pushd "${OPENIM_ROOT}" >/dev/null + make build BINS="codescan" + popd >/dev/null +fi + +if [[ ! -f "${CODESCAN_BINARY}" ]]; then + echo "Failed to build codescan binary." + exit 1 +fi + +CONFIG_PATH="${OPENIM_ROOT}/tools/codescan/config.yaml" + +"${CODESCAN_BINARY}" -config "${CONFIG_PATH}" \ No newline at end of file diff --git a/scripts/verify-pkg-names.sh b/scripts/verify-pkg-names.sh index 7fce3d7ad..be1acd015 100755 --- a/scripts/verify-pkg-names.sh +++ b/scripts/verify-pkg-names.sh @@ -18,9 +18,6 @@ # Usage: `scripts/verify-pkg-names.sh`. - - - OPENIM_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. source "${OPENIM_ROOT}/scripts/lib/init.sh" diff --git a/test/codescan/main.go b/test/codescan/main.go deleted file mode 100644 index 06ab7d0f9..000000000 --- a/test/codescan/main.go +++ /dev/null @@ -1 +0,0 @@ -package main diff --git a/tools/codescan/checker/checker.go b/tools/codescan/checker/checker.go new file mode 100644 index 000000000..953236d0f --- /dev/null +++ b/tools/codescan/checker/checker.go @@ -0,0 +1,90 @@ +package checker + +import ( + "bufio" + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/openimsdk/open-im-server/tools/codescan/config" +) + +type CheckResult struct { + FilePath string + Lines []int +} + +func checkFileForChineseComments(filePath string) ([]CheckResult, error) { + file, err := os.Open(filePath) + if err != nil { + return nil, err + } + defer file.Close() + + var results []CheckResult + scanner := bufio.NewScanner(file) + reg := regexp.MustCompile(`[\p{Han}]+`) + lineNumber := 0 + + var linesWithChinese []int + for scanner.Scan() { + lineNumber++ + if reg.FindString(scanner.Text()) != "" { + linesWithChinese = append(linesWithChinese, lineNumber) + } + } + + if len(linesWithChinese) > 0 { + results = append(results, CheckResult{ + FilePath: filePath, + Lines: linesWithChinese, + }) + } + + if err := scanner.Err(); err != nil { + return nil, err + } + + return results, nil +} + +func WalkDirAndCheckComments(cfg config.Config) error { + var allResults []CheckResult + err := filepath.Walk(cfg.Directory, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + for _, fileType := range cfg.FileTypes { + if filepath.Ext(path) == fileType { + results, err := checkFileForChineseComments(path) + if err != nil { + return err + } + if len(results) > 0 { + allResults = append(allResults, results...) + } + } + } + return nil + }) + + if err != nil { + return err + } + + if len(allResults) > 0 { + var errMsg strings.Builder + errMsg.WriteString("Files containing Chinese comments:\n") + for _, result := range allResults { + errMsg.WriteString(fmt.Sprintf("%s: Lines %v\n", result.FilePath, result.Lines)) + } + return fmt.Errorf(errMsg.String()) + } + + return nil +} diff --git a/tools/codescan/codescan.go b/tools/codescan/codescan.go new file mode 100644 index 000000000..c46f7a704 --- /dev/null +++ b/tools/codescan/codescan.go @@ -0,0 +1,20 @@ +package main + +import ( + "log" + + "github.com/openimsdk/open-im-server/tools/codescan/checker" + "github.com/openimsdk/open-im-server/tools/codescan/config" +) + +func main() { + cfg, err := config.ParseConfig() + if err != nil { + log.Fatalf("Error parsing config: %v", err) + } + + err = checker.WalkDirAndCheckComments(cfg) + if err != nil { + panic(err) + } +} diff --git a/tools/codescan/config.yaml b/tools/codescan/config.yaml new file mode 100644 index 000000000..9a81236b8 --- /dev/null +++ b/tools/codescan/config.yaml @@ -0,0 +1,7 @@ +directory: ./ +file_types: + - .go + - .yaml + - .yml +languages: + - Chinese \ No newline at end of file diff --git a/tools/codescan/config/config.go b/tools/codescan/config/config.go new file mode 100644 index 000000000..8f051e3da --- /dev/null +++ b/tools/codescan/config/config.go @@ -0,0 +1,35 @@ +package config + +import ( + "flag" + "log" + "os" + + "gopkg.in/yaml.v2" +) + +type Config struct { + Directory string `yaml:"directory"` + FileTypes []string `yaml:"file_types"` + Languages []string `yaml:"languages"` +} + +func ParseConfig() (Config, error) { + var configPath string + flag.StringVar(&configPath, "config", "./", "Path to config file") + flag.Parse() + + var config Config + if configPath != "" { + configFile, err := os.ReadFile(configPath) + if err != nil { + return Config{}, err + } + if err := yaml.Unmarshal(configFile, &config); err != nil { + return Config{}, err + } + } else { + log.Fatal("Config file must be provided") + } + return config, nil +} diff --git a/tools/codescan/go.mod b/tools/codescan/go.mod new file mode 100644 index 000000000..2ad132101 --- /dev/null +++ b/tools/codescan/go.mod @@ -0,0 +1,3 @@ +module github.com/openimsdk/open-im-server/tools/codescan + +go 1.19 From 88bcabee1ccde76ef376cfa2f732363330e89dd4 Mon Sep 17 00:00:00 2001 From: longyuqing112 <105913803+longyuqing112@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:20:25 +0800 Subject: [PATCH 64/74] feat: optimize openim make lint style format (#1995) * feat: add openim make lint * feat: add openim make lint * feat: add openim make lint * feat: add openim make lint * feat: add openim make lint --- .../msgtransfer/online_history_msg_handler.go | 3 ++- internal/push/offlinepush/fcm/push.go | 1 - .../kubernetes/kubernetes.go | 2 +- pkg/common/kafka/producer.go | 2 +- test/e2e/api/token/token.go | 14 ++++++++++++ test/e2e/framework/helpers/chat/chat.go | 22 +++++++++---------- 6 files changed, 29 insertions(+), 15 deletions(-) diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 5023ac008..635fe2eac 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -257,7 +257,8 @@ func (och *OnlineHistoryRedisConsumerHandler) toPushTopic( msgs []*sdkws.MsgData, ) { for _, v := range msgs { - och.msgDatabase.MsgToPushMQ(ctx, key, conversationID, v) + och.msgDatabase.MsgToPushMQ(ctx, key, conversationID, v) // nolint: errcheck + } } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index a60570860..b1f16b4b2 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -127,7 +127,6 @@ func (f *Fcm) Push(ctx context.Context, userIDs []string, title, content string, response, err := f.fcmMsgCli.SendAll(ctx, messages) if err != nil { Fail = Fail + messageCount - // log.Info(operationID, "some token push err", err.Error(), messageCount) } else { Success = Success + response.SuccessCount Fail = Fail + response.FailureCount diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index 7c40399a3..ab8864c6f 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -194,5 +194,5 @@ func (cli *K8sDR) GetClientLocalConns() map[string][]*grpc.ClientConn { return nil } func (cli *K8sDR) Close() { - return + } diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 4b5ce6b52..81064ab5f 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -118,7 +118,7 @@ func configureProducerAck(p *Producer, ackConfig string) { // configureCompression configures the message compression type for the producer. func configureCompression(p *Producer, compressType string) { - var compress sarama.CompressionCodec = sarama.CompressionNone + var compress = sarama.CompressionNone err := compress.UnmarshalText(bytes.ToLower([]byte(compressType))) if err != nil { fmt.Printf("Failed to configure compression: %v\n", err) diff --git a/test/e2e/api/token/token.go b/test/e2e/api/token/token.go index b4dbda330..908f9b8a0 100644 --- a/test/e2e/api/token/token.go +++ b/test/e2e/api/token/token.go @@ -57,6 +57,20 @@ type UserRegisterRequest struct { Users []User `json:"users"` } +/* func main() { + // Example usage of functions + token, err := GetUserToken("openIM123456") + if err != nil { + log.Fatalf("Error getting user token: %v", err) + } + fmt.Println("Token:", token) + + err = RegisterUser(token, "testUserID", "TestNickname", "https://example.com/image.jpg") + if err != nil { + log.Fatalf("Error registering user: %v", err) + } +} */ + // GetUserToken requests a user token from the API. func GetUserToken(userID string) (string, error) { reqBody := UserTokenRequest{ diff --git a/test/e2e/framework/helpers/chat/chat.go b/test/e2e/framework/helpers/chat/chat.go index 82dcc1aab..aa37c34b5 100644 --- a/test/e2e/framework/helpers/chat/chat.go +++ b/test/e2e/framework/helpers/chat/chat.go @@ -101,23 +101,23 @@ func main() { select {} } -// // getLatestVersion fetches the latest version number from a given URL. -// func getLatestVersion(url string) (string, error) { -// resp, err := http.Get(url) -// if err != nil { -// return "", err -// } -// defer resp.Body.Close() +// getLatestVersion fetches the latest version number from a given URL. +/* func getLatestVersion(url string) (string, error) { + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() // location := resp.Header.Get("Location") // if location == "" { // return defaultTemplateVersion, nil // } -// // Extract the version number from the URL -// latestVersion := filepath.Base(location) -// return latestVersion, nil -// } + // Extract the version number from the URL + latestVersion := filepath.Base(location) + return latestVersion, nil +} */ // downloadAndExtract downloads a file from a URL and extracts it to a destination directory. func downloadAndExtract(url, destDir string) error { From 33a61f29f20ee7326e755ea5d84500d96c77b5c3 Mon Sep 17 00:00:00 2001 From: Xinwei Xiong <3293172751NSS@gmail.com> Date: Mon, 4 Mar 2024 21:32:07 +0800 Subject: [PATCH 65/74] feat: format openim make lint code (#1997) --- .golangci.yml | 16 ++++++++- cmd/openim-api/main.go | 6 ++-- internal/api/auth.go | 3 +- internal/api/conversation.go | 3 +- internal/api/custom_validator.go | 3 +- internal/api/friend.go | 3 +- internal/api/group.go | 3 +- internal/api/msg.go | 3 +- internal/api/route.go | 15 ++++---- internal/api/statistics.go | 3 +- internal/api/third.go | 6 ++-- internal/msggateway/client.go | 10 +++--- internal/msggateway/hub_server.go | 3 +- internal/msggateway/message_handler.go | 8 ++--- internal/msggateway/n_ws_server.go | 10 +++--- internal/msgtransfer/init.go | 9 ++--- .../msgtransfer/online_history_msg_handler.go | 11 +++--- .../online_msg_to_mongo_handler.go | 3 +- internal/push/offlinepush/fcm/push.go | 3 +- internal/push/offlinepush/getui/push.go | 3 +- internal/push/push_handler.go | 3 +- internal/push/push_rpc_server.go | 6 ++-- internal/push/push_to_client.go | 6 ++-- internal/rpc/auth/auth.go | 6 ++-- internal/rpc/conversation/conversaion.go | 14 +++----- internal/rpc/friend/black.go | 3 +- internal/rpc/friend/friend.go | 20 ++++------- internal/rpc/group/callback.go | 6 ++-- internal/rpc/group/db_map.go | 3 +- internal/rpc/group/group.go | 34 +++++++------------ internal/rpc/msg/as_read.go | 10 +++--- internal/rpc/msg/callback.go | 6 ++-- internal/rpc/msg/delete.go | 4 +-- internal/rpc/msg/revoke.go | 3 +- internal/rpc/msg/send.go | 6 ++-- internal/rpc/msg/server.go | 3 +- internal/rpc/msg/sync_msg.go | 7 ++-- internal/rpc/third/s3.go | 9 ++--- internal/rpc/third/third.go | 17 ++++------ internal/rpc/third/tool.go | 4 +-- internal/rpc/user/user.go | 25 +++++--------- internal/tools/conversation.go | 1 - internal/tools/cron_task.go | 1 - internal/tools/msg.go | 19 ++++------- pkg/common/cmd/msg_gateway.go | 6 ++-- pkg/common/cmd/msg_transfer.go | 3 +- pkg/common/cmd/root.go | 9 ++--- pkg/common/cmd/rpc.go | 7 ++-- pkg/common/db/cache/conversation.go | 3 +- pkg/common/db/cache/friend.go | 3 +- pkg/common/db/cache/group.go | 5 +-- pkg/common/db/cache/init_redis.go | 3 +- pkg/common/db/cache/meta_cache.go | 6 ++-- pkg/common/db/cache/msg.go | 12 +++---- pkg/common/db/cache/user.go | 9 ++--- pkg/common/db/controller/auth.go | 9 ++--- pkg/common/db/controller/black.go | 3 +- pkg/common/db/controller/conversation.go | 6 ++-- pkg/common/db/controller/friend.go | 3 +- pkg/common/db/controller/group.go | 5 ++- pkg/common/db/controller/msg.go | 15 +++----- pkg/common/db/controller/user.go | 12 +++---- pkg/common/db/mgo/conversation.go | 3 +- pkg/common/db/mgo/friend.go | 3 +- pkg/common/db/mgo/friend_request.go | 3 +- pkg/common/db/mgo/group.go | 1 - pkg/common/db/mgo/group_member.go | 3 +- pkg/common/db/mgo/group_request.go | 1 - pkg/common/db/mgo/user.go | 3 +- pkg/common/db/s3/cont/controller.go | 6 ++-- pkg/common/db/s3/minio/minio.go | 3 +- pkg/common/db/table/relation/user.go | 1 - pkg/common/db/table/unrelation/msg.go | 4 +-- pkg/common/db/unrelation/mongo.go | 5 ++- pkg/common/db/unrelation/msg.go | 11 +++--- .../discoveryregister/discoveryregister.go | 7 ++-- .../kubernetes/kubernetes.go | 6 ++-- .../discoveryregister/zookeeper/zookeeper.go | 3 +- pkg/common/kafka/consumer_group.go | 3 +- pkg/common/kafka/producer.go | 6 ++-- pkg/common/startrpc/start.go | 19 ++++------- pkg/rpcclient/auth.go | 3 +- pkg/rpcclient/conversation.go | 6 ++-- pkg/rpcclient/friend.go | 3 +- pkg/rpcclient/group.go | 3 +- pkg/rpcclient/msg.go | 6 ++-- pkg/rpcclient/notification/friend.go | 3 +- pkg/rpcclient/notification/group.go | 3 +- pkg/rpcclient/push.go | 3 +- pkg/rpcclient/third.go | 7 ++-- pkg/rpcclient/user.go | 8 ++--- 91 files changed, 220 insertions(+), 375 deletions(-) mode change 100755 => 100644 pkg/rpcclient/notification/group.go mode change 100755 => 100644 pkg/rpcclient/third.go diff --git a/.golangci.yml b/.golangci.yml index 67419d05e..5bf9eeba9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -118,7 +118,6 @@ linters-settings: right-to-left-isolate: true first-strong-isolate: true pop-directional-isolate: true - dogsled: # checks assignments with too many blank identifiers; default is 2 max-blank-identifiers: 2 dupl: @@ -729,6 +728,21 @@ linters: - gofmt # Format check - govet # Go's standard linting tool - gosimple # Suggestions for simplifying code + - errcheck + - decorder + - sloglint + - ineffassign + - revive + - reassign + - tparallel + - unconvert + - dupl + - dupword + - errname + - gci + - goheader + - goprintffuncname + - gosec - misspell # Spelling mistakes - staticcheck # Static analysis - unused # Checks for unused code diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index eeaa04d07..ad4e3b5c4 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -26,12 +26,9 @@ import ( "syscall" "time" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" @@ -40,6 +37,7 @@ import ( kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { diff --git a/internal/api/auth.go b/internal/api/auth.go index 88539f63a..4aeadd790 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -15,10 +15,9 @@ package api import ( - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/auth" "github.com/OpenIMSDK/tools/a2r" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/conversation.go b/internal/api/conversation.go index eb735e550..c9101fb26 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -15,10 +15,9 @@ package api import ( - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/a2r" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/custom_validator.go b/internal/api/custom_validator.go index 1df4169e4..d135b0dc4 100644 --- a/internal/api/custom_validator.go +++ b/internal/api/custom_validator.go @@ -15,9 +15,8 @@ package api import ( - "github.com/go-playground/validator/v10" - "github.com/OpenIMSDK/protocol/constant" + "github.com/go-playground/validator/v10" ) // RequiredIf validates if the specified field is required based on the session type. diff --git a/internal/api/friend.go b/internal/api/friend.go index 7dc898a02..98e86a037 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -17,10 +17,9 @@ package api import ( "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/tools/a2r" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - - "github.com/gin-gonic/gin" ) type FriendApi rpcclient.Friend diff --git a/internal/api/group.go b/internal/api/group.go index e525cd495..af90c2db4 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -17,10 +17,9 @@ package api import ( "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/tools/a2r" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - - "github.com/gin-gonic/gin" ) type GroupApi rpcclient.Group diff --git a/internal/api/msg.go b/internal/api/msg.go index 1bbcf5428..4b253f56b 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -28,10 +28,9 @@ import ( "github.com/go-playground/validator/v10" "github.com/mitchellh/mapstructure" + "github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/route.go b/internal/api/route.go index a6d73b683..c6e0f53aa 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -21,13 +21,11 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/apiresp" + "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/tokenverify" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" @@ -35,11 +33,10 @@ import ( "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mw" - + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/statistics.go b/internal/api/statistics.go index 5750a7315..397523e88 100644 --- a/internal/api/statistics.go +++ b/internal/api/statistics.go @@ -15,10 +15,9 @@ package api import ( - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/a2r" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/third.go b/internal/api/third.go index 0a1ef0fbe..2acf1ce89 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -19,15 +19,13 @@ import ( "net/http" "strconv" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/a2r" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" + "github.com/gin-gonic/gin" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 06efea12f..7017cf7d4 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -22,18 +22,16 @@ import ( "sync" "sync/atomic" - "github.com/OpenIMSDK/tools/errs" - - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/apiresp" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/protobuf/proto" + + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) var ( diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index 807c4af3b..f6228717d 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -17,8 +17,6 @@ package msggateway import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msggateway" "github.com/OpenIMSDK/tools/discoveryregistry" @@ -26,6 +24,7 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 74bde1ff5..1cf91f67d 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -18,17 +18,15 @@ import ( "context" "sync" + "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/push" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - + "github.com/OpenIMSDK/tools/utils" "github.com/go-playground/validator/v10" "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/msg" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index f466a7dc4..bf8a0da39 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -25,18 +25,16 @@ import ( "sync/atomic" "time" - "github.com/OpenIMSDK/tools/apiresp" - - "github.com/go-playground/validator/v10" - "github.com/redis/go-redis/v9" - "golang.org/x/sync/errgroup" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msggateway" + "github.com/OpenIMSDK/tools/apiresp" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "github.com/go-playground/validator/v10" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index ca9ab5ac4..077282f83 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -18,16 +18,12 @@ import ( "context" "errors" "fmt" - - "github.com/OpenIMSDK/tools/errs" - - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - "net/http" "os" "os/signal" "syscall" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" @@ -42,6 +38,7 @@ import ( kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type MsgTransfer struct { @@ -121,7 +118,7 @@ func (m *MsgTransfer) Start(prometheusPort int) error { var ( netDone = make(chan struct{}, 1) - netErr error + netErr error ) go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH) diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 635fe2eac..e06ab5f07 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -22,23 +22,20 @@ import ( "sync/atomic" "time" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - - "github.com/OpenIMSDK/tools/errs" - "github.com/IBM/sarama" - "github.com/go-redis/redis" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "github.com/go-redis/redis" + "google.golang.org/protobuf/proto" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index 6e6c4c819..35c0a4a4c 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -18,10 +18,9 @@ import ( "context" "github.com/IBM/sarama" - "google.golang.org/protobuf/proto" - pbmsg "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/log" + "google.golang.org/protobuf/proto" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index b1f16b4b2..a3453ba5a 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -20,11 +20,10 @@ import ( firebase "firebase.google.com/go" "firebase.google.com/go/messaging" + "github.com/OpenIMSDK/protocol/constant" "github.com/redis/go-redis/v9" "google.golang.org/api/option" - "github.com/OpenIMSDK/protocol/constant" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index 8115e4efb..81dbfaee3 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -23,12 +23,11 @@ import ( "sync" "time" - "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils/splitter" + "github.com/redis/go-redis/v9" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 19d42ebb9..18b987267 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -18,13 +18,12 @@ import ( "context" "github.com/IBM/sarama" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/constant" pbchat "github.com/OpenIMSDK/protocol/msg" pbpush "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/protobuf/proto" "github.com/openimsdk/open-im-server/v3/pkg/common/config" kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index f558aeec3..30896849a 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -17,14 +17,12 @@ package push import ( "context" - "github.com/OpenIMSDK/tools/utils" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" pbpush "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 1140e8ce4..6e767a6be 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -20,10 +20,6 @@ import ( "errors" "sync" - "google.golang.org/grpc" - - "golang.org/x/sync/errgroup" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msggateway" @@ -32,6 +28,8 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index cde1e6ac5..774717cf9 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -17,10 +17,6 @@ package auth import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - - "google.golang.org/grpc" - pbauth "github.com/OpenIMSDK/protocol/auth" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msggateway" @@ -29,7 +25,9 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/tokenverify" + "google.golang.org/grpc" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index 582f93246..f513d1a2c 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -19,26 +19,22 @@ import ( "errors" "sort" - "github.com/OpenIMSDK/protocol/sdkws" - - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" pbconversation "github.com/OpenIMSDK/protocol/conversation" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" ) diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index ed5791c38..979cfdc95 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -18,11 +18,10 @@ import ( "context" "time" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/tools/mcontext" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 9c49af220..f3aea14f5 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -17,30 +17,24 @@ package friend import ( "context" - "github.com/OpenIMSDK/tools/tx" - - "github.com/OpenIMSDK/protocol/sdkws" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - - "github.com/OpenIMSDK/tools/log" - - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" + "github.com/OpenIMSDK/protocol/sdkws" registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" ) diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index d891f4d1e..2a0ce6584 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -18,16 +18,14 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/group" + pbgroup "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/wrapperspb" + "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - pbgroup "github.com/OpenIMSDK/protocol/group" - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/rpc/group/db_map.go b/internal/rpc/group/db_map.go index 07084873c..e4c18bb17 100644 --- a/internal/rpc/group/db_map.go +++ b/internal/rpc/group/db_map.go @@ -18,10 +18,9 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/mcontext" - pbgroup "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/mcontext" ) func UpdateGroupInfoMap(ctx context.Context, group *sdkws.GroupInfoForSet) map[string]any { diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index db5108f31..d8a1e1d56 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -23,40 +23,32 @@ import ( "strings" "time" - "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" - - pbconversation "github.com/OpenIMSDK/protocol/conversation" - "github.com/OpenIMSDK/protocol/wrapperspb" - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/grouphash" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" - - "github.com/OpenIMSDK/tools/mw/specialerror" - - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" + pbconversation "github.com/OpenIMSDK/protocol/conversation" pbgroup "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/protocol/wrapperspb" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" + "github.com/OpenIMSDK/tools/mw/specialerror" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" + "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/grouphash" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index cb292421e..f9a44577b 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -17,17 +17,15 @@ package msg import ( "context" - utils2 "github.com/OpenIMSDK/tools/utils" - - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" - - "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + utils2 "github.com/OpenIMSDK/tools/utils" + "github.com/redis/go-redis/v9" + + cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" ) func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) { diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index f98318bba..e281e4828 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -17,17 +17,15 @@ package msg import ( "context" - "github.com/OpenIMSDK/protocol/sdkws" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/constant" pbchat "github.com/OpenIMSDK/protocol/msg" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/protobuf/proto" cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go index b5c23bed6..e2d81c92a 100644 --- a/internal/rpc/msg/delete.go +++ b/internal/rpc/msg/delete.go @@ -17,14 +17,14 @@ package msg import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) func (m *msgServer) getMinSeqs(maxSeqs map[string]int64) map[string]int64 { diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 0a24753b2..2272911bc 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -19,8 +19,6 @@ import ( "encoding/json" "time" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" @@ -29,6 +27,7 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 630b74a4a..6f590578c 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -17,9 +17,6 @@ package msg import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/OpenIMSDK/protocol/constant" pbconversation "github.com/OpenIMSDK/protocol/conversation" pbmsg "github.com/OpenIMSDK/protocol/msg" @@ -29,6 +26,9 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, error error) { diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index c25879276..0643abe26 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,12 +15,11 @@ package msg import ( - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/discoveryregistry" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index dbd8da4d8..3b76db1e4 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -17,15 +17,14 @@ package msg import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" - "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) func (m *msgServer) PullMessageBySeqs( diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 3b501d4ad..488deb19e 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -23,18 +23,15 @@ import ( "strconv" "time" - "github.com/google/uuid" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "github.com/google/uuid" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 0d474d2c4..dfa77f9d3 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -20,22 +20,19 @@ import ( "net/url" "time" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go index a6c16ff9d..efa047a43 100644 --- a/internal/rpc/third/tool.go +++ b/internal/rpc/third/tool.go @@ -21,11 +21,11 @@ import ( "strings" "unicode/utf8" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) func toPbMapArray(m map[string][]string) []*third.KeyValues { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 6f9e2949f..b4140420b 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -21,35 +21,28 @@ import ( "strings" "time" - "github.com/OpenIMSDK/tools/pagination" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + pbuser "github.com/OpenIMSDK/protocol/user" + registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/pagination" + "github.com/OpenIMSDK/tools/tx" + "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - - registry "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" - - pbuser "github.com/OpenIMSDK/protocol/user" - "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" ) type userServer struct { diff --git a/internal/tools/conversation.go b/internal/tools/conversation.go index 6b918c0d1..5550bbad2 100644 --- a/internal/tools/conversation.go +++ b/internal/tools/conversation.go @@ -20,7 +20,6 @@ import ( "time" "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index 0544f2f85..8d198ff28 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -23,7 +23,6 @@ import ( "time" "github.com/OpenIMSDK/tools/errs" - "github.com/redis/go-redis/v9" "github.com/robfig/cron/v3" diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 7548e3c04..6d5a50b1e 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -18,30 +18,25 @@ import ( "context" "fmt" "math" - - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - - "github.com/redis/go-redis/v9" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - - kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - "math/rand" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/mw" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "github.com/redis/go-redis/v9" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" ) diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 317844770..1543afe15 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -17,13 +17,11 @@ package cmd import ( "errors" - "github.com/spf13/cobra" - - "github.com/openimsdk/open-im-server/v3/internal/msggateway" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" + "github.com/spf13/cobra" + "github.com/openimsdk/open-im-server/v3/internal/msggateway" v3config "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index 545c7543b..6ea2b86fa 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -20,9 +20,8 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/spf13/cobra" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/internal/msgtransfer" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type MsgTransferCmd struct { diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 4f4c5e69b..69a2af950 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -17,16 +17,13 @@ package cmd import ( "fmt" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/OpenIMSDK/tools/errs" - - "github.com/spf13/cobra" - "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/spf13/cobra" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type RootCmdPt interface { diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index 39bf9c036..d78233cbc 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -19,15 +19,12 @@ import ( "fmt" "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" "github.com/spf13/cobra" "google.golang.org/grpc" - "github.com/OpenIMSDK/tools/errs" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" ) diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index 047188947..c8cc11923 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -20,11 +20,10 @@ import ( "strings" "time" + "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/tools/utils" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index a2b60d48f..91faddaf9 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -18,11 +18,10 @@ import ( "context" "time" + "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/tools/utils" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 57fcf1a9b..0acf95ed4 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -22,14 +22,11 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - + "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/tools/utils" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index a41a4f460..24de092b0 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -22,10 +22,9 @@ import ( "strings" "time" - "github.com/redis/go-redis/v9" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw/specialerror" + "github.com/redis/go-redis/v9" "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index fee982dc5..5f05ff166 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -20,13 +20,11 @@ import ( "errors" "time" - "github.com/OpenIMSDK/tools/mw/specialerror" - - "github.com/dtm-labs/rockscache" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mw/specialerror" "github.com/OpenIMSDK/tools/utils" + "github.com/dtm-labs/rockscache" ) const ( diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 5cef509ed..09c456ab3 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -20,21 +20,17 @@ import ( "strconv" "time" - "golang.org/x/sync/errgroup" - - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - - "github.com/gogo/protobuf/jsonpb" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "github.com/gogo/protobuf/jsonpb" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/redis/go-redis/v9" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) const ( diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index f444a2c46..11c46b5ac 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -22,17 +22,14 @@ import ( "strconv" "time" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/errs" - + "github.com/OpenIMSDK/tools/log" "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" + + relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) const ( diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 13bc066ca..04c5d2617 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -17,15 +17,12 @@ package controller import ( "context" - "github.com/OpenIMSDK/tools/errs" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - - "github.com/golang-jwt/jwt/v4" - "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/tokenverify" + "github.com/golang-jwt/jwt/v4" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go index 3c8a47a01..427194ecd 100644 --- a/pkg/common/db/controller/black.go +++ b/pkg/common/db/controller/black.go @@ -17,9 +17,8 @@ package controller import ( "context" - "github.com/OpenIMSDK/tools/pagination" - "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index 0c64169a7..bf4349cf4 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -18,17 +18,15 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/pagination" - - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) type ConversationDatabase interface { diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index bb5ec3087..24504c227 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -19,12 +19,11 @@ import ( "fmt" "time" - "github.com/OpenIMSDK/tools/pagination" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" + "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 6f208cc24..2f2a972e4 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -18,12 +18,11 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/pagination" - "github.com/dtm-labs/rockscache" - "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "github.com/dtm-labs/rockscache" "github.com/redis/go-redis/v9" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index add9b63ce..07b540ec0 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -21,14 +21,12 @@ import ( "time" "github.com/OpenIMSDK/protocol/constant" - - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - - "github.com/redis/go-redis/v9" - + pbmsg "github.com/OpenIMSDK/protocol/msg" + "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - + "github.com/OpenIMSDK/tools/utils" + "github.com/redis/go-redis/v9" "go.mongodb.org/mongo-driver/mongo" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -37,10 +35,7 @@ import ( unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" - - pbmsg "github.com/OpenIMSDK/protocol/msg" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" ) const ( diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index 8ba1c01d3..c18fef316 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -18,19 +18,15 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/pagination" - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - "github.com/OpenIMSDK/protocol/user" - - unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" - "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/pagination" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) type UserDatabase interface { diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index d0a46ae47..93ca4eceb 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -18,9 +18,8 @@ import ( "context" "time" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go index 851db6157..01719822d 100644 --- a/pkg/common/db/mgo/friend.go +++ b/pkg/common/db/mgo/friend.go @@ -19,10 +19,9 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/mgo/friend_request.go b/pkg/common/db/mgo/friend_request.go index bfc101917..a05ca1018 100644 --- a/pkg/common/db/mgo/friend_request.go +++ b/pkg/common/db/mgo/friend_request.go @@ -19,10 +19,9 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" - "go.mongodb.org/mongo-driver/mongo/options" - "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" + "go.mongodb.org/mongo-driver/mongo/options" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go index 922bfd424..cfb104a21 100644 --- a/pkg/common/db/mgo/group.go +++ b/pkg/common/db/mgo/group.go @@ -19,7 +19,6 @@ import ( "time" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/db/mgo/group_member.go b/pkg/common/db/mgo/group_member.go index e28432b11..5d3331baa 100644 --- a/pkg/common/db/mgo/group_member.go +++ b/pkg/common/db/mgo/group_member.go @@ -17,9 +17,8 @@ package mgo import ( "context" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/db/mgo/group_request.go b/pkg/common/db/mgo/group_request.go index d20682239..87fba1636 100644 --- a/pkg/common/db/mgo/group_request.go +++ b/pkg/common/db/mgo/group_request.go @@ -18,7 +18,6 @@ import ( "context" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index 2797bc53f..df26776ac 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -20,11 +20,10 @@ import ( "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/errs" - "go.mongodb.org/mongo-driver/bson/primitive" - "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" "go.mongodb.org/mongo-driver/bson" + "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go index f60c2c650..0a40d8f36 100644 --- a/pkg/common/db/s3/cont/controller.go +++ b/pkg/common/db/s3/cont/controller.go @@ -24,13 +24,11 @@ import ( "strings" "time" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - - "github.com/google/uuid" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/google/uuid" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index 81545ff1b..a5e34e1aa 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -29,14 +29,13 @@ import ( "time" "unsafe" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/OpenIMSDK/tools/log" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/signer" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/table/relation/user.go b/pkg/common/db/table/relation/user.go index 265c3c93a..375930ddf 100644 --- a/pkg/common/db/table/relation/user.go +++ b/pkg/common/db/table/relation/user.go @@ -19,7 +19,6 @@ import ( "time" "github.com/OpenIMSDK/protocol/user" - "github.com/OpenIMSDK/tools/pagination" ) diff --git a/pkg/common/db/table/unrelation/msg.go b/pkg/common/db/table/unrelation/msg.go index c95b211a8..60dd7c260 100644 --- a/pkg/common/db/table/unrelation/msg.go +++ b/pkg/common/db/table/unrelation/msg.go @@ -20,10 +20,8 @@ import ( "time" "github.com/OpenIMSDK/protocol/msg" - - "go.mongodb.org/mongo-driver/mongo" - "github.com/OpenIMSDK/protocol/sdkws" + "go.mongodb.org/mongo-driver/mongo" ) const ( diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 4c093b3c3..25bf4d10c 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -21,13 +21,12 @@ import ( "strings" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/mw/specialerror" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/mw/specialerror" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 0a6f683a5..0e33970c3 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -21,20 +21,17 @@ import ( "fmt" "time" - "github.com/OpenIMSDK/protocol/msg" - "github.com/OpenIMSDK/protocol/constant" - + "github.com/OpenIMSDK/protocol/msg" + "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/sdkws" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go index 4ade0147a..18684c349 100644 --- a/pkg/common/discoveryregister/discoveryregister.go +++ b/pkg/common/discoveryregister/discoveryregister.go @@ -18,13 +18,12 @@ import ( "errors" "os" - "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" - - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/errs" ) // NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type. diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index ab8864c6f..59a78a336 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -22,12 +22,10 @@ import ( "strconv" "strings" - "github.com/stathat/consistent" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" + "github.com/stathat/consistent" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index 6e55b6b8b..099badf42 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -20,10 +20,9 @@ import ( "strings" "time" - "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/discoveryregistry" openkeeper "github.com/OpenIMSDK/tools/discoveryregistry/zookeeper" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index ed961b93e..f8b3ee249 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,13 +17,12 @@ package kafka import ( "context" "errors" + "strings" "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "strings" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 81064ab5f..19ff19828 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -18,19 +18,17 @@ import ( "bytes" "context" "errors" + "fmt" "strings" "time" - "github.com/OpenIMSDK/tools/errs" - "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "google.golang.org/protobuf/proto" - "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index e19456d86..07b50070f 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -27,25 +27,20 @@ import ( "syscall" "time" + "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - + "github.com/OpenIMSDK/tools/mw" + "github.com/OpenIMSDK/tools/network" + grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - - grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/mw" - "github.com/OpenIMSDK/tools/network" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) // Start rpc server. diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index 810b7ae93..387acf320 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -17,10 +17,9 @@ package rpcclient import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/auth" "github.com/OpenIMSDK/tools/discoveryregistry" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 9afa45004..926c571a4 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -18,15 +18,13 @@ import ( "context" "fmt" - "google.golang.org/grpc" - pbconversation "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) type Conversation struct { diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index 39026b10c..a0e0226ec 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -17,11 +17,10 @@ package rpcclient import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/friend" sdkws "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index cb61579be..7a15c1116 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -18,14 +18,13 @@ import ( "context" "strings" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 6804e78a2..f468a6569 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -18,18 +18,16 @@ import ( "context" "encoding/json" - "google.golang.org/grpc" - "google.golang.org/protobuf/proto" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" "github.com/openimsdk/open-im-server/v3/pkg/common/config" - // "google.golang.org/protobuf/proto". ) func newContentTypeConf() map[int32]config.NotificationConf { diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index a6b693724..ba7a77c55 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -17,11 +17,10 @@ package notification import ( "context" - "github.com/OpenIMSDK/tools/mcontext" - "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/OpenIMSDK/tools/mcontext" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go old mode 100755 new mode 100644 index d336e4b12..8b355b663 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -18,8 +18,6 @@ import ( "context" "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/constant" pbgroup "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/protocol/sdkws" @@ -28,6 +26,7 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index b5a5c49cf..20296e6cd 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -17,10 +17,9 @@ package rpcclient import ( "context" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/discoveryregistry" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go old mode 100755 new mode 100644 index 05b825060..f6ca458fd --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -18,13 +18,12 @@ import ( "context" "net/url" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + "google.golang.org/grpc" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 810a88ad0..8d0f7fc8b 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -18,18 +18,16 @@ import ( "context" "strings" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" + "google.golang.org/grpc" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) // User represents a structure holding connection details for the User RPC client. From e8377d9c2c248dd94a30bc317c4ebdb4dd75bc19 Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:51:55 +0800 Subject: [PATCH 66/74] cicd: bump League Patch (#1998) --- CHANGELOG/CHANGELOG-3.5.md | 16 +---- cmd/openim-api/main.go | 1 - internal/api/auth.go | 1 - internal/api/conversation.go | 1 - internal/api/friend.go | 1 - internal/api/group.go | 1 - internal/api/msg.go | 1 - internal/api/route.go | 13 +++-- internal/api/statistics.go | 1 - internal/api/third.go | 1 - internal/api/user.go | 1 - internal/msggateway/callback.go | 1 - internal/msggateway/client.go | 3 +- internal/msggateway/compressor_test.go | 2 +- internal/msggateway/hub_server.go | 3 +- internal/msggateway/message_handler.go | 3 +- internal/msggateway/n_ws_server.go | 5 +- internal/msgtransfer/init.go | 11 ++-- .../msgtransfer/online_history_msg_handler.go | 3 +- .../online_msg_to_mongo_handler.go | 3 +- internal/push/callback.go | 1 - internal/push/offlinepush/fcm/push.go | 5 +- internal/push/offlinepush/getui/push.go | 3 +- internal/push/push_handler.go | 3 +- internal/push/push_rpc_server.go | 3 +- internal/push/push_to_client.go | 5 +- internal/rpc/auth/auth.go | 3 +- internal/rpc/conversation/conversaion.go | 3 +- internal/rpc/friend/black.go | 1 - internal/rpc/friend/callback.go | 1 - internal/rpc/friend/friend.go | 3 +- internal/rpc/group/cache.go | 1 - internal/rpc/group/callback.go | 1 - internal/rpc/group/convert.go | 1 - internal/rpc/group/group.go | 3 +- internal/rpc/msg/as_read.go | 3 +- internal/rpc/msg/callback.go | 3 +- internal/rpc/msg/delete.go | 1 - internal/rpc/msg/message_interceptor.go | 1 - internal/rpc/msg/revoke.go | 1 - internal/rpc/msg/send.go | 1 - internal/rpc/msg/server.go | 3 +- internal/rpc/msg/statistics.go | 1 - internal/rpc/msg/sync_msg.go | 1 - internal/rpc/msg/utils.go | 3 +- internal/rpc/msg/verify.go | 1 - internal/rpc/third/log.go | 1 - internal/rpc/third/s3.go | 1 - internal/rpc/third/third.go | 3 +- internal/rpc/third/tool.go | 1 - internal/rpc/user/callback.go | 1 - internal/rpc/user/user.go | 3 +- internal/tools/conversation.go | 1 - internal/tools/cron_task.go | 5 +- internal/tools/msg.go | 7 +-- internal/tools/msg_doc_convert.go | 1 - pkg/authverify/token.go | 1 - pkg/callbackstruct/group.go | 1 - pkg/common/cmd/api.go | 3 +- pkg/common/cmd/msg_gateway.go | 3 +- pkg/common/cmd/msg_transfer.go | 3 +- pkg/common/cmd/msg_utils.go | 3 +- pkg/common/cmd/root.go | 3 +- pkg/common/cmd/rpc.go | 5 +- pkg/common/config/parse.go | 3 +- pkg/common/convert/black.go | 1 - pkg/common/convert/conversation.go | 1 - pkg/common/convert/friend.go | 7 ++- pkg/common/convert/group.go | 1 - pkg/common/convert/msg.go | 1 - pkg/common/convert/user.go | 1 - pkg/common/db/cache/black.go | 3 +- pkg/common/db/cache/conversation.go | 3 +- pkg/common/db/cache/friend.go | 3 +- pkg/common/db/cache/group.go | 3 +- pkg/common/db/cache/init_redis.go | 3 +- pkg/common/db/cache/meta_cache.go | 9 ++- pkg/common/db/cache/msg.go | 5 +- pkg/common/db/cache/s3.go | 3 +- pkg/common/db/cache/user.go | 3 +- pkg/common/db/controller/auth.go | 1 - pkg/common/db/controller/black.go | 1 - pkg/common/db/controller/conversation.go | 4 +- pkg/common/db/controller/friend.go | 1 - pkg/common/db/controller/group.go | 3 +- pkg/common/db/controller/msg.go | 8 +-- pkg/common/db/controller/s3.go | 3 +- pkg/common/db/controller/third.go | 1 - pkg/common/db/controller/user.go | 1 - pkg/common/db/localcache/conversation.go | 1 - pkg/common/db/localcache/group.go | 1 - pkg/common/db/mgo/black.go | 3 +- pkg/common/db/mgo/conversation.go | 3 +- pkg/common/db/mgo/friend.go | 3 +- pkg/common/db/mgo/friend_request.go | 3 +- pkg/common/db/mgo/group.go | 3 +- pkg/common/db/mgo/group_member.go | 3 +- pkg/common/db/mgo/group_request.go | 3 +- pkg/common/db/mgo/log.go | 3 +- pkg/common/db/mgo/object.go | 3 +- pkg/common/db/mgo/user.go | 3 +- pkg/common/db/s3/aws/aws.go | 1 - pkg/common/db/s3/cont/controller.go | 1 - pkg/common/db/s3/cos/cos.go | 3 +- pkg/common/db/s3/minio/minio.go | 1 - pkg/common/db/s3/minio/thumbnail.go | 1 - pkg/common/db/s3/oss/oss.go | 1 - pkg/common/db/unrelation/mongo.go | 5 +- pkg/common/db/unrelation/msg.go | 3 +- pkg/common/db/unrelation/msg_convert.go | 3 +- pkg/common/db/unrelation/user.go | 3 +- .../discoveryregister/direct/directconn.go | 3 +- .../discoveryregister/discoveryregister.go | 1 - .../kubernetes/kubernetes.go | 3 +- .../discoveryregister/zookeeper/zookeeper.go | 1 - pkg/common/http/http_client.go | 1 - pkg/common/kafka/consumer_group.go | 1 - pkg/common/kafka/producer.go | 3 +- pkg/common/kafka/util.go | 1 - pkg/common/prommetrics/prommetrics.go | 5 +- pkg/common/startrpc/start.go | 9 ++- pkg/common/tls/tls.go | 1 - pkg/rpcclient/auth.go | 3 +- pkg/rpcclient/conversation.go | 3 +- pkg/rpcclient/friend.go | 3 +- pkg/rpcclient/group.go | 3 +- pkg/rpcclient/msg.go | 3 +- pkg/rpcclient/notification/conversation.go | 1 - pkg/rpcclient/notification/friend.go | 1 - pkg/rpcclient/notification/group.go | 1 - pkg/rpcclient/notification/msg.go | 1 - pkg/rpcclient/notification/user.go | 1 - pkg/rpcclient/push.go | 3 +- pkg/rpcclient/third.go | 3 +- pkg/rpcclient/user.go | 3 +- test/e2e/page/chat_page.go | 14 +++++ test/e2e/page/login_page.go | 14 +++++ tools/codescan/checker/checker.go | 14 +++++ tools/codescan/codescan.go | 14 +++++ tools/codescan/config.yaml | 14 +++++ tools/codescan/config/config.go | 14 +++++ tools/formitychecker/checker/checker.go | 1 + tools/url2im/main.go | 58 +++++++++---------- 143 files changed, 230 insertions(+), 272 deletions(-) diff --git a/CHANGELOG/CHANGELOG-3.5.md b/CHANGELOG/CHANGELOG-3.5.md index 0a7f5bfa3..b929922f1 100644 --- a/CHANGELOG/CHANGELOG-3.5.md +++ b/CHANGELOG/CHANGELOG-3.5.md @@ -11,18 +11,9 @@ ## [v3.5.1-alpha.2] - 2024-01-26 - -## [v3.5.0+15.d356f7a] - 2024-01-26 - ## [v3.5.1-rc.1] - 2024-01-23 - -## [v3.5.0+2.e0bd54f-3-g52f9fc209] - 2024-01-12 - - -## [v3.5.0+2.e0bd54f-1-g4ce6a0fa6] - 2024-01-12 - ## [v3.5.1-alpha.1] - 2024-01-09 @@ -75,11 +66,8 @@ [Unreleased]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.2...HEAD -[v3.5.1-alpha.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+15.d356f7a...v3.5.1-alpha.2 -[v3.5.0+15.d356f7a]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.1...v3.5.0+15.d356f7a -[v3.5.1-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+2.e0bd54f-3-g52f9fc209...v3.5.1-rc.1 -[v3.5.0+2.e0bd54f-3-g52f9fc209]: https://github.com/openimsdk/open-im-server/compare/v3.5.0+2.e0bd54f-1-g4ce6a0fa6...v3.5.0+2.e0bd54f-3-g52f9fc209 -[v3.5.0+2.e0bd54f-1-g4ce6a0fa6]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...v3.5.0+2.e0bd54f-1-g4ce6a0fa6 +[v3.5.1-alpha.2]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-rc.1...v3.5.1-alpha.2 +[v3.5.1-rc.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-alpha.1...v3.5.1-rc.1 [v3.5.1-alpha.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.0...v3.5.1-alpha.1 [v3.5.0]: https://github.com/openimsdk/open-im-server/compare/v3.5.1...v3.5.0 [v3.5.1]: https://github.com/openimsdk/open-im-server/compare/v3.5.1-bate.1...v3.5.1 diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index ad4e3b5c4..0fb609aa3 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -29,7 +29,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/api/auth.go b/internal/api/auth.go index 4aeadd790..0f7a3933b 100644 --- a/internal/api/auth.go +++ b/internal/api/auth.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/auth" "github.com/OpenIMSDK/tools/a2r" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/conversation.go b/internal/api/conversation.go index c9101fb26..fe6c67001 100644 --- a/internal/api/conversation.go +++ b/internal/api/conversation.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/a2r" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/friend.go b/internal/api/friend.go index 98e86a037..24bcbf899 100644 --- a/internal/api/friend.go +++ b/internal/api/friend.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/tools/a2r" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/group.go b/internal/api/group.go index af90c2db4..c18ded64b 100644 --- a/internal/api/group.go +++ b/internal/api/group.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/tools/a2r" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/msg.go b/internal/api/msg.go index 4b253f56b..61cd1ae6c 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -27,7 +27,6 @@ import ( "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/mitchellh/mapstructure" - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/api/route.go b/internal/api/route.go index c6e0f53aa..5c920ca05 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -29,19 +29,22 @@ import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" - "github.com/redis/go-redis/v9" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine { - discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) // Default RPC middleware + discov.AddOption( + mw.GrpcClient(), + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")), + ) // Default RPC middleware gin.SetMode(gin.ReleaseMode) r := gin.New() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { diff --git a/internal/api/statistics.go b/internal/api/statistics.go index 397523e88..2b80a1585 100644 --- a/internal/api/statistics.go +++ b/internal/api/statistics.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/a2r" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/third.go b/internal/api/third.go index 2acf1ce89..f00c0d8d3 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -24,7 +24,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" "github.com/gin-gonic/gin" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/user.go b/internal/api/user.go index 16efcd704..3cc5470a7 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index 7d5381754..ede48f74a 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -20,7 +20,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/mcontext" - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" diff --git a/internal/msggateway/client.go b/internal/msggateway/client.go index 7017cf7d4..4e843821e 100644 --- a/internal/msggateway/client.go +++ b/internal/msggateway/client.go @@ -29,9 +29,8 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "google.golang.org/protobuf/proto" ) var ( diff --git a/internal/msggateway/compressor_test.go b/internal/msggateway/compressor_test.go index bb7106d9f..173c9bb20 100644 --- a/internal/msggateway/compressor_test.go +++ b/internal/msggateway/compressor_test.go @@ -111,7 +111,7 @@ func BenchmarkDecompress(b *testing.B) { compressor := NewGzipCompressor() comdata, err := compressor.Compress(src) - + assert.Equal(b, nil, err) for i := 0; i < b.N; i++ { diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index f6228717d..826c2488b 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -24,12 +24,11 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" + "google.golang.org/grpc" ) func (s *Server) InitServer(disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 1cf91f67d..105a77336 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -25,9 +25,8 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" "github.com/go-playground/validator/v10" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/protobuf/proto" ) type Req struct { diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index bf8a0da39..e8ba9939a 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -33,14 +33,13 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" "github.com/go-playground/validator/v10" - "github.com/redis/go-redis/v9" - "golang.org/x/sync/errgroup" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" ) type LongConnServer interface { diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 077282f83..dfb7c5307 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -25,12 +25,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -39,6 +33,11 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) type MsgTransfer struct { diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index e06ab5f07..4995d10e8 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -30,13 +30,12 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" "github.com/go-redis/redis" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/protobuf/proto" ) const ( diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index 35c0a4a4c..efffc191f 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -20,12 +20,11 @@ import ( "github.com/IBM/sarama" pbmsg "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/log" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "google.golang.org/protobuf/proto" ) type OnlineHistoryMongoConsumerHandler struct { diff --git a/internal/push/callback.go b/internal/push/callback.go index 46b5545fb..70862e4d2 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -21,7 +21,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index a3453ba5a..aa6ec186f 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -21,12 +21,11 @@ import ( firebase "firebase.google.com/go" "firebase.google.com/go/messaging" "github.com/OpenIMSDK/protocol/constant" - "github.com/redis/go-redis/v9" - "google.golang.org/api/option" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/redis/go-redis/v9" + "google.golang.org/api/option" ) const SinglePushCountLimit = 400 diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index 81dbfaee3..e950585a1 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -27,12 +27,11 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils/splitter" - "github.com/redis/go-redis/v9" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" + "github.com/redis/go-redis/v9" ) var ( diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 18b987267..7b7a1150a 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -23,10 +23,9 @@ import ( pbpush "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" kfk "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" + "google.golang.org/protobuf/proto" ) type ConsumerHandler struct { diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index 30896849a..c5516e7cf 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -22,12 +22,11 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) type pushServer struct { diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 6e767a6be..84efdba0c 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -28,9 +28,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "golang.org/x/sync/errgroup" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/dummy" "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush/fcm" @@ -43,6 +40,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "golang.org/x/sync/errgroup" + "google.golang.org/grpc" ) type Pusher struct { diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 774717cf9..5f53911a4 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -25,14 +25,13 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/tokenverify" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) type authServer struct { diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index f513d1a2c..f3dc7ef17 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -27,8 +27,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -37,6 +35,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "google.golang.org/grpc" ) type conversationServer struct { diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 979cfdc95..57edd26ba 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -20,7 +20,6 @@ import ( pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" diff --git a/internal/rpc/friend/callback.go b/internal/rpc/friend/callback.go index e5054d9a9..2b6e31899 100644 --- a/internal/rpc/friend/callback.go +++ b/internal/rpc/friend/callback.go @@ -19,7 +19,6 @@ import ( pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/tools/utils" - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index f3aea14f5..4168731f7 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -25,8 +25,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" @@ -36,6 +34,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "google.golang.org/grpc" ) type friendServer struct { diff --git a/internal/rpc/group/cache.go b/internal/rpc/group/cache.go index fc387736d..54b60c554 100644 --- a/internal/rpc/group/cache.go +++ b/internal/rpc/group/cache.go @@ -18,7 +18,6 @@ import ( "context" pbgroup "github.com/OpenIMSDK/protocol/group" - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" ) diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index 2a0ce6584..f09c23ec6 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/internal/rpc/group/convert.go b/internal/rpc/group/convert.go index 2ee7f5056..ab4d3a2a1 100644 --- a/internal/rpc/group/convert.go +++ b/internal/rpc/group/convert.go @@ -16,7 +16,6 @@ package group import ( "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index d8a1e1d56..fa060efda 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -35,8 +35,6 @@ import ( "github.com/OpenIMSDK/tools/mw/specialerror" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" @@ -49,6 +47,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/grouphash" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index f9a44577b..cac9102fa 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -23,9 +23,8 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" utils2 "github.com/OpenIMSDK/tools/utils" - "github.com/redis/go-redis/v9" - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" + "github.com/redis/go-redis/v9" ) func (m *msgServer) GetConversationsHasReadAndMaxSeq(ctx context.Context, req *msg.GetConversationsHasReadAndMaxSeqReq) (resp *msg.GetConversationsHasReadAndMaxSeqResp, err error) { diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index e281e4828..1a7cad70c 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -23,11 +23,10 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/protobuf/proto" - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" + "google.golang.org/protobuf/proto" ) func cbURL() string { diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go index e2d81c92a..091f11f4c 100644 --- a/internal/rpc/msg/delete.go +++ b/internal/rpc/msg/delete.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) diff --git a/internal/rpc/msg/message_interceptor.go b/internal/rpc/msg/message_interceptor.go index a98c31219..3a2731fea 100644 --- a/internal/rpc/msg/message_interceptor.go +++ b/internal/rpc/msg/message_interceptor.go @@ -21,7 +21,6 @@ import ( "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 2272911bc..eebfdf779 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -26,7 +26,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 6f590578c..e04cebb3b 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -26,7 +26,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 0643abe26..79ae483a2 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -19,13 +19,12 @@ import ( "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/discoveryregistry" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) type ( diff --git a/internal/rpc/msg/statistics.go b/internal/rpc/msg/statistics.go index ac09e3f69..d7d33459b 100644 --- a/internal/rpc/msg/statistics.go +++ b/internal/rpc/msg/statistics.go @@ -21,7 +21,6 @@ import ( "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index 3b76db1e4..fa894e034 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -22,7 +22,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) diff --git a/internal/rpc/msg/utils.go b/internal/rpc/msg/utils.go index e45d7b395..d8a45e875 100644 --- a/internal/rpc/msg/utils.go +++ b/internal/rpc/msg/utils.go @@ -18,10 +18,9 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/redis/go-redis/v9" "go.mongodb.org/mongo-driver/mongo" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) func isMessageHasReadEnabled(msgData *sdkws.MsgData) bool { diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 0080b6fdb..11055fac1 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/internal/rpc/third/log.go b/internal/rpc/third/log.go index 11c7467b8..420f399ba 100644 --- a/internal/rpc/third/log.go +++ b/internal/rpc/third/log.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" utils2 "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 488deb19e..7f68f4da8 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -29,7 +29,6 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" "github.com/google/uuid" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont" diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index dfa77f9d3..9dd3ffd65 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -22,8 +22,6 @@ import ( "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -34,6 +32,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go index efa047a43..cf25f9820 100644 --- a/internal/rpc/third/tool.go +++ b/internal/rpc/third/tool.go @@ -24,7 +24,6 @@ import ( "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go index 5276946a4..1437257f7 100644 --- a/internal/rpc/user/callback.go +++ b/internal/rpc/user/callback.go @@ -19,7 +19,6 @@ import ( pbuser "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/utils" - cbapi "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/http" diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index b4140420b..7ed3ff7d6 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -30,8 +30,6 @@ import ( "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" @@ -43,6 +41,7 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "google.golang.org/grpc" ) type userServer struct { diff --git a/internal/tools/conversation.go b/internal/tools/conversation.go index 5550bbad2..b555a3361 100644 --- a/internal/tools/conversation.go +++ b/internal/tools/conversation.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index 8d198ff28..9b74a5767 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -23,11 +23,10 @@ import ( "time" "github.com/OpenIMSDK/tools/errs" - "github.com/redis/go-redis/v9" - "github.com/robfig/cron/v3" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/redis/go-redis/v9" + "github.com/robfig/cron/v3" ) func StartTask() error { diff --git a/internal/tools/msg.go b/internal/tools/msg.go index 6d5a50b1e..f2df0d337 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -27,10 +27,6 @@ import ( "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/redis/go-redis/v9" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" @@ -39,6 +35,9 @@ import ( kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "github.com/redis/go-redis/v9" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) type MsgTool struct { diff --git a/internal/tools/msg_doc_convert.go b/internal/tools/msg_doc_convert.go index b9150c362..eea1b69e8 100644 --- a/internal/tools/msg_doc_convert.go +++ b/internal/tools/msg_doc_convert.go @@ -18,7 +18,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index cfba2d844..0a46af3ec 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/tokenverify" "github.com/OpenIMSDK/tools/utils" "github.com/golang-jwt/jwt/v4" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/callbackstruct/group.go b/pkg/callbackstruct/group.go index 5968f1e55..467061a4a 100644 --- a/pkg/callbackstruct/group.go +++ b/pkg/callbackstruct/group.go @@ -16,7 +16,6 @@ package callbackstruct import ( common "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/apistruct" ) diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index 7156ce6c4..47e4116e3 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -19,9 +19,8 @@ import ( "fmt" "github.com/OpenIMSDK/protocol/constant" - "github.com/spf13/cobra" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" ) type ApiCmd struct { diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 1543afe15..403d0ec84 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -19,10 +19,9 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" - "github.com/spf13/cobra" - "github.com/openimsdk/open-im-server/v3/internal/msggateway" v3config "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" ) type MsgGatewayCmd struct { diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index 6ea2b86fa..4db24fac5 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -18,10 +18,9 @@ import ( "fmt" "github.com/OpenIMSDK/protocol/constant" - "github.com/spf13/cobra" - "github.com/openimsdk/open-im-server/v3/internal/msgtransfer" config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" ) type MsgTransferCmd struct { diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index 3c670ad85..03c1cab67 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -15,10 +15,9 @@ package cmd import ( - "github.com/spf13/cobra" - "github.com/openimsdk/open-im-server/v3/internal/tools" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/spf13/cobra" ) type MsgUtilsCmd struct { diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 69a2af950..591bfc804 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -20,10 +20,9 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/spf13/cobra" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" ) type RootCmdPt interface { diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index d78233cbc..e30de93a9 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -21,11 +21,10 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/spf13/cobra" - "google.golang.org/grpc" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" + "github.com/spf13/cobra" + "google.golang.org/grpc" ) type RpcCmd struct { diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index e216f6014..a73665386 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -22,10 +22,9 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" - "gopkg.in/yaml.v3" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "gopkg.in/yaml.v3" ) //go:embed version diff --git a/pkg/common/convert/black.go b/pkg/common/convert/black.go index 57140f436..683517fbc 100644 --- a/pkg/common/convert/black.go +++ b/pkg/common/convert/black.go @@ -19,7 +19,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" sdk "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/convert/conversation.go b/pkg/common/convert/conversation.go index 165262b7f..b3f4913eb 100644 --- a/pkg/common/convert/conversation.go +++ b/pkg/common/convert/conversation.go @@ -17,7 +17,6 @@ package convert import ( "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/convert/friend.go b/pkg/common/convert/friend.go index 0e9a05766..4231eb49d 100644 --- a/pkg/common/convert/friend.go +++ b/pkg/common/convert/friend.go @@ -20,7 +20,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) @@ -53,7 +52,11 @@ func FriendDB2Pb(ctx context.Context, friendDB *relation.FriendModel, }, nil } -func FriendsDB2Pb(ctx context.Context, friendsDB []*relation.FriendModel, getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error)) (friendsPb []*sdkws.FriendInfo, err error) { +func FriendsDB2Pb( + ctx context.Context, + friendsDB []*relation.FriendModel, + getUsers func(ctx context.Context, userIDs []string) (map[string]*sdkws.UserInfo, error), +) (friendsPb []*sdkws.FriendInfo, err error) { if len(friendsDB) == 0 { return nil, nil } diff --git a/pkg/common/convert/group.go b/pkg/common/convert/group.go index 18940f518..63372f21d 100644 --- a/pkg/common/convert/group.go +++ b/pkg/common/convert/group.go @@ -19,7 +19,6 @@ import ( pbgroup "github.com/OpenIMSDK/protocol/group" sdkws "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/convert/msg.go b/pkg/common/convert/msg.go index 56f71f018..34638049b 100644 --- a/pkg/common/convert/msg.go +++ b/pkg/common/convert/msg.go @@ -17,7 +17,6 @@ package convert import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/pkg/common/convert/user.go b/pkg/common/convert/user.go index 62f80e458..38afd8c19 100644 --- a/pkg/common/convert/user.go +++ b/pkg/common/convert/user.go @@ -18,7 +18,6 @@ import ( "time" "github.com/OpenIMSDK/protocol/sdkws" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/cache/black.go b/pkg/common/db/cache/black.go index 48bc3c285..c8cf2ddd3 100644 --- a/pkg/common/db/cache/black.go +++ b/pkg/common/db/cache/black.go @@ -19,9 +19,8 @@ import ( "time" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) const ( diff --git a/pkg/common/db/cache/conversation.go b/pkg/common/db/cache/conversation.go index c8cc11923..90e400363 100644 --- a/pkg/common/db/cache/conversation.go +++ b/pkg/common/db/cache/conversation.go @@ -22,9 +22,8 @@ import ( "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) const ( diff --git a/pkg/common/db/cache/friend.go b/pkg/common/db/cache/friend.go index 91faddaf9..a160837ba 100644 --- a/pkg/common/db/cache/friend.go +++ b/pkg/common/db/cache/friend.go @@ -20,9 +20,8 @@ import ( "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) const ( diff --git a/pkg/common/db/cache/group.go b/pkg/common/db/cache/group.go index 0acf95ed4..4a5266db5 100644 --- a/pkg/common/db/cache/group.go +++ b/pkg/common/db/cache/group.go @@ -25,9 +25,8 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) const ( diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index 24de092b0..b81edf9e5 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -24,9 +24,8 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw/specialerror" - "github.com/redis/go-redis/v9" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/redis/go-redis/v9" ) var ( diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 5f05ff166..4c25754d6 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -194,7 +194,14 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin // return tArrays, nil //} -func batchGetCache2[T any, K comparable](ctx context.Context, rcClient *rockscache.Client, expire time.Duration, keys []K, keyFn func(key K) string, fns func(ctx context.Context, key K) (T, error)) ([]T, error) { +func batchGetCache2[T any, K comparable]( + ctx context.Context, + rcClient *rockscache.Client, + expire time.Duration, + keys []K, + keyFn func(key K) string, + fns func(ctx context.Context, key K) (T, error), +) ([]T, error) { if len(keys) == 0 { return nil, nil } diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 09c456ab3..889f36baa 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -26,11 +26,10 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" "github.com/gogo/protobuf/jsonpb" - "github.com/redis/go-redis/v9" - "golang.org/x/sync/errgroup" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/redis/go-redis/v9" + "golang.org/x/sync/errgroup" ) const ( diff --git a/pkg/common/db/cache/s3.go b/pkg/common/db/cache/s3.go index 1e68cedf8..28e993be0 100644 --- a/pkg/common/db/cache/s3.go +++ b/pkg/common/db/cache/s3.go @@ -20,10 +20,9 @@ import ( "time" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) type ObjectCache interface { diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 11c46b5ac..c6c6966f3 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -27,9 +27,8 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) const ( diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 04c5d2617..dfd7b3e78 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -21,7 +21,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/tokenverify" "github.com/golang-jwt/jwt/v4" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) diff --git a/pkg/common/db/controller/black.go b/pkg/common/db/controller/black.go index 427194ecd..c4b253c2f 100644 --- a/pkg/common/db/controller/black.go +++ b/pkg/common/db/controller/black.go @@ -20,7 +20,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/controller/conversation.go b/pkg/common/db/controller/conversation.go index bf4349cf4..3d46e4fbc 100644 --- a/pkg/common/db/controller/conversation.go +++ b/pkg/common/db/controller/conversation.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" @@ -42,7 +41,8 @@ type ConversationDatabase interface { GetUserAllConversation(ctx context.Context, ownerUserID string) ([]*relationtb.ConversationModel, error) // SetUserConversations sets multiple conversation properties for a user, creates new conversations if they do not exist, or updates them otherwise. This operation is atomic. SetUserConversations(ctx context.Context, ownerUserID string, conversations []*relationtb.ConversationModel) error - // SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is transactional. + // SetUsersConversationFieldTx updates a specific field for multiple users' conversations, creating new conversations if they do not exist, or updates them otherwise. This operation is + // transactional. SetUsersConversationFieldTx(ctx context.Context, userIDs []string, conversation *relationtb.ConversationModel, fieldMap map[string]any) error // CreateGroupChatConversation creates a group chat conversation for the specified group ID and user IDs. CreateGroupChatConversation(ctx context.Context, groupID string, userIDs []string) error diff --git a/pkg/common/db/controller/friend.go b/pkg/common/db/controller/friend.go index 24504c227..3c81d922c 100644 --- a/pkg/common/db/controller/friend.go +++ b/pkg/common/db/controller/friend.go @@ -26,7 +26,6 @@ import ( "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/controller/group.go b/pkg/common/db/controller/group.go index 2f2a972e4..45bf87b6f 100644 --- a/pkg/common/db/controller/group.go +++ b/pkg/common/db/controller/group.go @@ -23,10 +23,9 @@ import ( "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" "github.com/dtm-labs/rockscache" - "github.com/redis/go-redis/v9" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) type GroupDatabase interface { diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index 07b540ec0..56cb52703 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -26,9 +26,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" - "github.com/redis/go-redis/v9" - "go.mongodb.org/mongo-driver/mongo" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" @@ -36,6 +33,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/redis/go-redis/v9" + "go.mongodb.org/mongo-driver/mongo" ) const ( @@ -61,7 +60,8 @@ type CommonMsgDatabase interface { GetMsgBySeqsRange(ctx context.Context, userID string, conversationID string, begin, end, num, userMaxSeq int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) // GetMsgBySeqs retrieves messages for large groups from MongoDB by sequence numbers. GetMsgBySeqs(ctx context.Context, userID string, conversationID string, seqs []int64) (minSeq int64, maxSeq int64, seqMsg []*sdkws.MsgData, err error) - // DeleteConversationMsgsAndSetMinSeq deletes conversation messages and resets the minimum sequence number. If `remainTime` is 0, all messages are deleted (this method does not delete Redis cache). + // DeleteConversationMsgsAndSetMinSeq deletes conversation messages and resets the minimum sequence number. If `remainTime` is 0, all messages are deleted (this method does not delete Redis + // cache). DeleteConversationMsgsAndSetMinSeq(ctx context.Context, conversationID string, remainTime int64) error // UserMsgsDestruct marks messages for deletion based on destruct time and returns a list of sequence numbers for marked messages. UserMsgsDestruct(ctx context.Context, userID string, conversationID string, destructTime int64, lastMsgDestructTime time.Time) (seqs []int64, err error) diff --git a/pkg/common/db/controller/s3.go b/pkg/common/db/controller/s3.go index 95505de41..e847c9c8f 100644 --- a/pkg/common/db/controller/s3.go +++ b/pkg/common/db/controller/s3.go @@ -19,12 +19,11 @@ import ( "path/filepath" "time" - "github.com/redis/go-redis/v9" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/redis/go-redis/v9" ) type S3Database interface { diff --git a/pkg/common/db/controller/third.go b/pkg/common/db/controller/third.go index 55c047bd6..996d82c45 100644 --- a/pkg/common/db/controller/third.go +++ b/pkg/common/db/controller/third.go @@ -19,7 +19,6 @@ import ( "time" "github.com/OpenIMSDK/tools/pagination" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) diff --git a/pkg/common/db/controller/user.go b/pkg/common/db/controller/user.go index c18fef316..0e1bdd314 100644 --- a/pkg/common/db/controller/user.go +++ b/pkg/common/db/controller/user.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/pagination" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" diff --git a/pkg/common/db/localcache/conversation.go b/pkg/common/db/localcache/conversation.go index c40bcdbce..9147fd3ce 100644 --- a/pkg/common/db/localcache/conversation.go +++ b/pkg/common/db/localcache/conversation.go @@ -19,7 +19,6 @@ import ( "sync" "github.com/OpenIMSDK/protocol/conversation" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/common/db/localcache/group.go b/pkg/common/db/localcache/group.go index 4958d91ee..0fdea8642 100644 --- a/pkg/common/db/localcache/group.go +++ b/pkg/common/db/localcache/group.go @@ -20,7 +20,6 @@ import ( "github.com/OpenIMSDK/protocol/group" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/common/db/mgo/black.go b/pkg/common/db/mgo/black.go index 1047e5c30..c555e0b77 100644 --- a/pkg/common/db/mgo/black.go +++ b/pkg/common/db/mgo/black.go @@ -19,11 +19,10 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewBlackMongo(db *mongo.Database) (relation.BlackModelInterface, error) { diff --git a/pkg/common/db/mgo/conversation.go b/pkg/common/db/mgo/conversation.go index 93ca4eceb..35e8a28e6 100644 --- a/pkg/common/db/mgo/conversation.go +++ b/pkg/common/db/mgo/conversation.go @@ -22,11 +22,10 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewConversationMongo(db *mongo.Database) (*ConversationMgo, error) { diff --git a/pkg/common/db/mgo/friend.go b/pkg/common/db/mgo/friend.go index 01719822d..aa7775ce0 100644 --- a/pkg/common/db/mgo/friend.go +++ b/pkg/common/db/mgo/friend.go @@ -19,11 +19,10 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) // FriendMgo implements FriendModelInterface using MongoDB as the storage backend. diff --git a/pkg/common/db/mgo/friend_request.go b/pkg/common/db/mgo/friend_request.go index a05ca1018..3e0588a0b 100644 --- a/pkg/common/db/mgo/friend_request.go +++ b/pkg/common/db/mgo/friend_request.go @@ -19,11 +19,10 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewFriendRequestMongo(db *mongo.Database) (relation.FriendRequestModelInterface, error) { diff --git a/pkg/common/db/mgo/group.go b/pkg/common/db/mgo/group.go index cfb104a21..1bef90ebe 100644 --- a/pkg/common/db/mgo/group.go +++ b/pkg/common/db/mgo/group.go @@ -21,11 +21,10 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewGroupMongo(db *mongo.Database) (relation.GroupModelInterface, error) { diff --git a/pkg/common/db/mgo/group_member.go b/pkg/common/db/mgo/group_member.go index 5d3331baa..e1af34f7c 100644 --- a/pkg/common/db/mgo/group_member.go +++ b/pkg/common/db/mgo/group_member.go @@ -21,11 +21,10 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewGroupMember(db *mongo.Database) (relation.GroupMemberModelInterface, error) { diff --git a/pkg/common/db/mgo/group_request.go b/pkg/common/db/mgo/group_request.go index 87fba1636..9aee0e960 100644 --- a/pkg/common/db/mgo/group_request.go +++ b/pkg/common/db/mgo/group_request.go @@ -20,11 +20,10 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewGroupRequestMgo(db *mongo.Database) (relation.GroupRequestModelInterface, error) { diff --git a/pkg/common/db/mgo/log.go b/pkg/common/db/mgo/log.go index 09f002ee3..ca28d5964 100644 --- a/pkg/common/db/mgo/log.go +++ b/pkg/common/db/mgo/log.go @@ -20,11 +20,10 @@ import ( "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewLogMongo(db *mongo.Database) (relation.LogInterface, error) { diff --git a/pkg/common/db/mgo/object.go b/pkg/common/db/mgo/object.go index 88bfde213..4c333afd6 100644 --- a/pkg/common/db/mgo/object.go +++ b/pkg/common/db/mgo/object.go @@ -18,11 +18,10 @@ import ( "context" "github.com/OpenIMSDK/tools/mgoutil" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewS3Mongo(db *mongo.Database) (relation.ObjectInfoModelInterface, error) { diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index df26776ac..dcdc14d4a 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -22,12 +22,11 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/OpenIMSDK/tools/pagination" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" ) func NewUserMongo(db *mongo.Database) (relation.UserModelInterface, error) { diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go index 7588eea91..dd54ed155 100644 --- a/pkg/common/db/s3/aws/aws.go +++ b/pkg/common/db/s3/aws/aws.go @@ -27,7 +27,6 @@ import ( "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/session" sdk "github.com/aws/aws-sdk-go/service/s3" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/s3/cont/controller.go b/pkg/common/db/s3/cont/controller.go index 0a40d8f36..915109aa5 100644 --- a/pkg/common/db/s3/cont/controller.go +++ b/pkg/common/db/s3/cont/controller.go @@ -27,7 +27,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/google/uuid" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go index b302f1de6..619f142ab 100644 --- a/pkg/common/db/s3/cos/cos.go +++ b/pkg/common/db/s3/cos/cos.go @@ -29,10 +29,9 @@ import ( "strings" "time" - "github.com/tencentyun/cos-go-sdk-v5" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" + "github.com/tencentyun/cos-go-sdk-v5" ) const ( diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index a5e34e1aa..1eb3257e1 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -33,7 +33,6 @@ import ( "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/signer" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" diff --git a/pkg/common/db/s3/minio/thumbnail.go b/pkg/common/db/s3/minio/thumbnail.go index 5dfdaee09..1bf96c27c 100644 --- a/pkg/common/db/s3/minio/thumbnail.go +++ b/pkg/common/db/s3/minio/thumbnail.go @@ -31,7 +31,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/minio/minio-go/v7" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go index a6be41d39..442f4e52f 100644 --- a/pkg/common/db/s3/oss/oss.go +++ b/pkg/common/db/s3/oss/oss.go @@ -32,7 +32,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/aliyun/aliyun-oss-go-sdk/oss" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 25bf4d10c..09880fb37 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -23,12 +23,11 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mw/specialerror" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) const ( diff --git a/pkg/common/db/unrelation/msg.go b/pkg/common/db/unrelation/msg.go index 0e33970c3..4deca56f8 100644 --- a/pkg/common/db/unrelation/msg.go +++ b/pkg/common/db/unrelation/msg.go @@ -26,13 +26,12 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "google.golang.org/protobuf/proto" - - table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) var ErrMsgListNotExist = errors.New("user not have msg in mongoDB") diff --git a/pkg/common/db/unrelation/msg_convert.go b/pkg/common/db/unrelation/msg_convert.go index 373bc843e..30c74e927 100644 --- a/pkg/common/db/unrelation/msg_convert.go +++ b/pkg/common/db/unrelation/msg_convert.go @@ -19,10 +19,9 @@ import ( "fmt" "github.com/OpenIMSDK/tools/log" + table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" - - table "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) func (m *MsgMongoDriver) ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) { diff --git a/pkg/common/db/unrelation/user.go b/pkg/common/db/unrelation/user.go index 4c882fc64..cbf395bf8 100644 --- a/pkg/common/db/unrelation/user.go +++ b/pkg/common/db/unrelation/user.go @@ -18,11 +18,10 @@ import ( "context" "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) // prefixes and suffixes. diff --git a/pkg/common/discoveryregister/direct/directconn.go b/pkg/common/discoveryregister/direct/directconn.go index cc59934a3..2ae0de170 100644 --- a/pkg/common/discoveryregister/direct/directconn.go +++ b/pkg/common/discoveryregister/direct/directconn.go @@ -20,10 +20,9 @@ import ( "fmt" "github.com/OpenIMSDK/tools/errs" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type ServiceAddresses map[string][]int diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go index 18684c349..a21d8d62a 100644 --- a/pkg/common/discoveryregister/discoveryregister.go +++ b/pkg/common/discoveryregister/discoveryregister.go @@ -20,7 +20,6 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index 59a78a336..1292c64a8 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -24,10 +24,9 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/stathat/consistent" "google.golang.org/grpc" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) // K8sDR represents the Kubernetes service discovery and registration client. diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index 099badf42..5f9a3b6bd 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -24,7 +24,6 @@ import ( openkeeper "github.com/OpenIMSDK/tools/discoveryregistry/zookeeper" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/http/http_client.go b/pkg/common/http/http_client.go index 141284b64..83908b8d3 100644 --- a/pkg/common/http/http_client.go +++ b/pkg/common/http/http_client.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index f8b3ee249..824c5be2e 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -22,7 +22,6 @@ import ( "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 19ff19828..4c2378c59 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -27,9 +27,8 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "google.golang.org/protobuf/proto" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "google.golang.org/protobuf/proto" ) const maxRetry = 10 // number of retries diff --git a/pkg/common/kafka/util.go b/pkg/common/kafka/util.go index 578a308ef..a94397819 100644 --- a/pkg/common/kafka/util.go +++ b/pkg/common/kafka/util.go @@ -20,7 +20,6 @@ import ( "strings" "github.com/IBM/sarama" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/tls" ) diff --git a/pkg/common/prommetrics/prommetrics.go b/pkg/common/prommetrics/prommetrics.go index 1ed13a1ef..52694168f 100644 --- a/pkg/common/prommetrics/prommetrics.go +++ b/pkg/common/prommetrics/prommetrics.go @@ -16,11 +16,10 @@ package prommetrics import ( gp "github.com/grpc-ecosystem/go-grpc-prometheus" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/collectors" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/collectors" ) func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *gp.ServerMetrics, error) { diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 07b50070f..1576762e8 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -32,15 +32,14 @@ import ( "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/network" grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) // Start rpc server. diff --git a/pkg/common/tls/tls.go b/pkg/common/tls/tls.go index e897c13b6..a52f46df7 100644 --- a/pkg/common/tls/tls.go +++ b/pkg/common/tls/tls.go @@ -22,7 +22,6 @@ import ( "os" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index 387acf320..bfd4b1119 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -19,10 +19,9 @@ import ( "github.com/OpenIMSDK/protocol/auth" "github.com/OpenIMSDK/tools/discoveryregistry" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry) *Auth { diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index 926c571a4..ee9818f8f 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -21,10 +21,9 @@ import ( pbconversation "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) type Conversation struct { diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index a0e0226ec..520616564 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -20,10 +20,9 @@ import ( "github.com/OpenIMSDK/protocol/friend" sdkws "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) type Friend struct { diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index 7a15c1116..bc9a3c75c 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -24,10 +24,9 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) type Group struct { diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index f468a6569..e4008ed20 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -24,10 +24,9 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "google.golang.org/grpc" "google.golang.org/protobuf/proto" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) func newContentTypeConf() map[int32]config.NotificationConf { diff --git a/pkg/rpcclient/notification/conversation.go b/pkg/rpcclient/notification/conversation.go index 3bbad746e..b43e5494a 100644 --- a/pkg/rpcclient/notification/conversation.go +++ b/pkg/rpcclient/notification/conversation.go @@ -19,7 +19,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index ba7a77c55..9237111be 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -21,7 +21,6 @@ import ( pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 8b355b663..1778a498d 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" diff --git a/pkg/rpcclient/notification/msg.go b/pkg/rpcclient/notification/msg.go index 60fa64f40..83280f80a 100644 --- a/pkg/rpcclient/notification/msg.go +++ b/pkg/rpcclient/notification/msg.go @@ -19,7 +19,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/rpcclient/notification/user.go b/pkg/rpcclient/notification/user.go index 13e5b6abb..f94e59a33 100644 --- a/pkg/rpcclient/notification/user.go +++ b/pkg/rpcclient/notification/user.go @@ -19,7 +19,6 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index 20296e6cd..2f540da81 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -19,10 +19,9 @@ import ( "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/discoveryregistry" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) type Push struct { diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go index f6ca458fd..be40335d5 100644 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -23,10 +23,9 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) type Third struct { diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index 8d0f7fc8b..a6c202129 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -23,11 +23,10 @@ import ( "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" - "google.golang.org/grpc" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "google.golang.org/grpc" ) // User represents a structure holding connection details for the User RPC client. diff --git a/test/e2e/page/chat_page.go b/test/e2e/page/chat_page.go index 52aa8377d..d92c7ec6e 100644 --- a/test/e2e/page/chat_page.go +++ b/test/e2e/page/chat_page.go @@ -1 +1,15 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package page diff --git a/test/e2e/page/login_page.go b/test/e2e/page/login_page.go index 52aa8377d..d92c7ec6e 100644 --- a/test/e2e/page/login_page.go +++ b/test/e2e/page/login_page.go @@ -1 +1,15 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package page diff --git a/tools/codescan/checker/checker.go b/tools/codescan/checker/checker.go index 953236d0f..ad724dd5b 100644 --- a/tools/codescan/checker/checker.go +++ b/tools/codescan/checker/checker.go @@ -1,3 +1,17 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package checker import ( diff --git a/tools/codescan/codescan.go b/tools/codescan/codescan.go index c46f7a704..a83e895fc 100644 --- a/tools/codescan/codescan.go +++ b/tools/codescan/codescan.go @@ -1,3 +1,17 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package main import ( diff --git a/tools/codescan/config.yaml b/tools/codescan/config.yaml index 9a81236b8..32a8c1f54 100644 --- a/tools/codescan/config.yaml +++ b/tools/codescan/config.yaml @@ -1,3 +1,17 @@ +# Copyright © 2024 OpenIM. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + directory: ./ file_types: - .go diff --git a/tools/codescan/config/config.go b/tools/codescan/config/config.go index 8f051e3da..aebf0d4f7 100644 --- a/tools/codescan/config/config.go +++ b/tools/codescan/config/config.go @@ -1,3 +1,17 @@ +// Copyright © 2024 OpenIM. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package config import ( diff --git a/tools/formitychecker/checker/checker.go b/tools/formitychecker/checker/checker.go index 9a64b7090..b17cc5427 100644 --- a/tools/formitychecker/checker/checker.go +++ b/tools/formitychecker/checker/checker.go @@ -22,6 +22,7 @@ import ( "strings" "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/tools/formitychecker/config" ) diff --git a/tools/url2im/main.go b/tools/url2im/main.go index 28c1e1c43..9e319932f 100644 --- a/tools/url2im/main.go +++ b/tools/url2im/main.go @@ -33,35 +33,35 @@ import ( func main() { var conf pkg.Config // Configuration object, '*' denotes required fields - // *Required*: Path for the task log file - flag.StringVar(&conf.TaskPath, "task", "take.txt", "Path for the task log file") - - // Optional: Path for the progress log file - flag.StringVar(&conf.ProgressPath, "progress", "", "Path for the progress log file") - - // Number of concurrent operations - flag.IntVar(&conf.Concurrency, "concurrency", 1, "Number of concurrent operations") - - // Number of retry attempts - flag.IntVar(&conf.Retry, "retry", 1, "Number of retry attempts") - - // Optional: Path for the temporary directory - flag.StringVar(&conf.TempDir, "temp", "", "Path for the temporary directory") - - // Cache size in bytes (downloads move to disk when exceeded) - flag.Int64Var(&conf.CacheSize, "cache", 1024*1024*100, "Cache size in bytes") - - // Request timeout in milliseconds - flag.Int64Var((*int64)(&conf.Timeout), "timeout", 5000, "Request timeout in milliseconds") - - // *Required*: API endpoint for the IM service - flag.StringVar(&conf.Api, "api", "http://127.0.0.1:10002", "API endpoint for the IM service") - - // IM administrator's user ID - flag.StringVar(&conf.UserID, "userID", "openIM123456", "IM administrator's user ID") - - // Secret for the IM configuration - flag.StringVar(&conf.Secret, "secret", "openIM123", "Secret for the IM configuration") + // *Required*: Path for the task log file + flag.StringVar(&conf.TaskPath, "task", "take.txt", "Path for the task log file") + + // Optional: Path for the progress log file + flag.StringVar(&conf.ProgressPath, "progress", "", "Path for the progress log file") + + // Number of concurrent operations + flag.IntVar(&conf.Concurrency, "concurrency", 1, "Number of concurrent operations") + + // Number of retry attempts + flag.IntVar(&conf.Retry, "retry", 1, "Number of retry attempts") + + // Optional: Path for the temporary directory + flag.StringVar(&conf.TempDir, "temp", "", "Path for the temporary directory") + + // Cache size in bytes (downloads move to disk when exceeded) + flag.Int64Var(&conf.CacheSize, "cache", 1024*1024*100, "Cache size in bytes") + + // Request timeout in milliseconds + flag.Int64Var((*int64)(&conf.Timeout), "timeout", 5000, "Request timeout in milliseconds") + + // *Required*: API endpoint for the IM service + flag.StringVar(&conf.Api, "api", "http://127.0.0.1:10002", "API endpoint for the IM service") + + // IM administrator's user ID + flag.StringVar(&conf.UserID, "userID", "openIM123456", "IM administrator's user ID") + + // Secret for the IM configuration + flag.StringVar(&conf.Secret, "secret", "openIM123", "Secret for the IM configuration") flag.Parse() if !filepath.IsAbs(conf.TaskPath) { From efb831053123772182f8275891a4937261fc7cee Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:27:26 +0800 Subject: [PATCH 67/74] Update readme and golangci lint (#1999) * Update README.md * Update .golangci.yml --------- Co-authored-by: Xinwei Xiong <3293172751NSS@gmail.com> --- .golangci.yml | 1 - README.md | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 5bf9eeba9..9c7960642 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -730,7 +730,6 @@ linters: - gosimple # Suggestions for simplifying code - errcheck - decorder - - sloglint - ineffassign - revive - reassign diff --git a/README.md b/README.md index 1552f62e5..175db3326 100644 --- a/README.md +++ b/README.md @@ -125,10 +125,10 @@ We support many platforms. Here are the addresses for quick experience on the we ## :hammer_and_wrench: To Start Developing OpenIM -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/openimsdk/open-im-server) - [![Open in Dev Container](https://img.shields.io/static/v1?label=Dev%20Container&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/github/openimsdk/open-im-server) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/openimsdk/open-im-server) + OpenIM Our goal is to build a top-level open source community. We have a set of standards, in the [Community repository](https://github.com/OpenIMSDK/community). If you'd like to contribute to this Open-IM-Server repository, please read our [contributor documentation](https://github.com/openimsdk/open-im-server/blob/main/CONTRIBUTING.md). From 383758782e114e22f4873a9573830bac41998933 Mon Sep 17 00:00:00 2001 From: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:53:22 +0800 Subject: [PATCH 68/74] =?UTF-8?q?optimization:=20change=20the=20configurat?= =?UTF-8?q?ion=20file=20from=20being=20read=20globally=20=E2=80=A6=20(#193?= =?UTF-8?q?5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * optimization: change the configuration file from being read globally to being read independently. * optimization: change the configuration file from being read globally to being read independently. * optimization: change the configuration file from being read globally to being read independently. * optimization: config file changed to dependency injection. * fix: replace global config with dependency injection * fix: replace global config with dependency injection * fix: import the enough param * fix: import the enough param * fix: import the enough param * fix: fix the component check of path * fix: fix the kafka of tls is nil problem * fix: fix the TLS.CACrt is nil error * fix: fix the valiable shadows problem * fix: fix the comflect * optimization: message remove options. * fix: fix the param pass error * fix: find error * fix: find error * fix: find eror * fix: find error * fix: find error * fix: del the undifined func * fix: find error * fix: fix the error * fix: pass config * fix: find error * fix: find error * fix: find error * fix: find error * fix: find error * fix: fix the config * fix: fix the error * fix: fix the config pass error * fix: fix the eror * fix: fix the error * fix: fix the error * fix: fix the error * fix: find error * fix: fix the error * fix: fix the config * fix: add return err * fix: fix the err2 * fix: err * fix: fix the func * fix: del the chinese comment * fix: fix the func * fix: fix the gateway_test logic * fix: s3 * test * test * fix: not found --------- Co-authored-by: luhaoling <2198702716@qq.com> Co-authored-by: withchao <993506633@qq.com> --- cmd/openim-api/main.go | 100 +------ cmd/openim-crontask/main.go | 3 +- cmd/openim-msggateway/main.go | 1 - cmd/openim-push/main.go | 6 +- cmd/openim-rpc/openim-rpc-auth/main.go | 7 +- .../openim-rpc-conversation/main.go | 6 +- cmd/openim-rpc/openim-rpc-friend/main.go | 6 +- cmd/openim-rpc/openim-rpc-group/main.go | 6 +- cmd/openim-rpc/openim-rpc-msg/main.go | 6 +- cmd/openim-rpc/openim-rpc-third/main.go | 6 +- cmd/openim-rpc/openim-rpc-user/main.go | 6 +- internal/api/msg.go | 6 +- internal/api/route.go | 149 ++++++++-- internal/api/third.go | 8 +- internal/api/user.go | 6 +- internal/msggateway/callback.go | 22 +- internal/msggateway/hub_server.go | 19 +- internal/msggateway/init.go | 15 +- internal/msggateway/message_handler.go | 7 +- internal/msggateway/n_ws_server.go | 24 +- internal/msgtransfer/init.go | 49 ++-- .../msgtransfer/online_history_msg_handler.go | 25 +- .../online_msg_to_mongo_handler.go | 24 +- internal/push/callback.go | 27 +- internal/push/consumer_init.go | 6 +- internal/push/offlinepush/fcm/push.go | 12 +- internal/push/offlinepush/getui/body.go | 6 +- internal/push/offlinepush/getui/push.go | 23 +- .../offlinepush/jpush/body/notification.go | 5 +- internal/push/offlinepush/jpush/push.go | 16 +- internal/push/push_handler.go | 22 +- internal/push/push_rpc_server.go | 20 +- internal/push/push_to_client.go | 44 +-- internal/rpc/auth/auth.go | 31 +- internal/rpc/conversation/conversaion.go | 19 +- internal/rpc/friend/black.go | 2 +- internal/rpc/friend/callback.go | 60 ++-- internal/rpc/friend/friend.go | 68 +++-- internal/rpc/group/callback.go | 89 +++--- internal/rpc/group/group.go | 89 +++--- internal/rpc/msg/as_read.go | 4 +- internal/rpc/msg/callback.go | 52 ++-- internal/rpc/msg/delete.go | 8 +- internal/rpc/msg/message_interceptor.go | 8 +- internal/rpc/msg/revoke.go | 21 +- internal/rpc/msg/send.go | 21 +- internal/rpc/msg/server.go | 51 ++-- internal/rpc/msg/sync_msg.go | 2 +- internal/rpc/msg/utils.go | 6 +- internal/rpc/msg/verify.go | 14 +- internal/rpc/third/log.go | 4 +- internal/rpc/third/s3.go | 11 +- internal/rpc/third/third.go | 50 ++-- internal/rpc/third/tool.go | 11 +- internal/rpc/user/callback.go | 36 +-- internal/rpc/user/user.go | 98 ++++--- internal/tools/cron_task.go | 25 +- internal/tools/cron_task_test.go | 35 ++- internal/tools/msg.go | 37 +-- pkg/authverify/token.go | 43 ++- pkg/common/cmd/api.go | 50 ++-- pkg/common/cmd/cron_task.go | 22 +- pkg/common/cmd/msg_gateway.go | 59 +--- pkg/common/cmd/msg_gateway_test.go | 2 +- pkg/common/cmd/msg_transfer.go | 31 +- pkg/common/cmd/msg_utils.go | 3 +- pkg/common/cmd/root.go | 47 ++- pkg/common/cmd/rpc.go | 180 +++++++----- pkg/common/config/config.go | 16 +- pkg/common/config/parse.go | 93 +++--- pkg/common/config/parse_test.go | 3 +- pkg/common/db/cache/init_redis.go | 42 +-- pkg/common/db/cache/meta_cache.go | 19 +- pkg/common/db/cache/msg.go | 23 +- pkg/common/db/cache/user.go | 32 +- pkg/common/db/controller/auth.go | 11 +- pkg/common/db/controller/msg.go | 35 ++- pkg/common/db/controller/msg_test.go | 56 ++-- pkg/common/db/s3/aws/aws.go | 275 ------------------ pkg/common/db/s3/cos/cos.go | 22 +- pkg/common/db/s3/minio/image.go | 90 ++++-- pkg/common/db/s3/minio/minio.go | 62 ++-- pkg/common/db/s3/oss/oss.go | 26 +- pkg/common/db/unrelation/mongo.go | 31 +- .../discoveryregister/direct/directconn.go | 36 +-- .../discoveryregister/discoveryregister.go | 13 +- .../discoveryregister_test.go | 16 +- .../kubernetes/kubernetes.go | 32 +- .../discoveryregister/zookeeper/zookeeper.go | 18 +- pkg/common/kafka/consumer.go | 37 ++- pkg/common/kafka/consumer_group.go | 19 +- pkg/common/kafka/producer.go | 23 +- pkg/common/kafka/util.go | 22 +- pkg/common/prommetrics/prommetrics.go | 10 +- pkg/common/prommetrics/prommetrics_test.go | 7 +- pkg/common/startrpc/start.go | 42 +-- pkg/common/startrpc/start_test.go | 6 +- pkg/common/tls/tls.go | 8 +- pkg/rpcclient/auth.go | 7 +- pkg/rpcclient/conversation.go | 11 +- pkg/rpcclient/friend.go | 11 +- pkg/rpcclient/group.go | 11 +- pkg/rpcclient/msg.go | 103 +++---- pkg/rpcclient/notification/conversation.go | 5 +- pkg/rpcclient/notification/friend.go | 4 +- pkg/rpcclient/notification/group.go | 36 +-- pkg/rpcclient/notification/msg.go | 5 +- pkg/rpcclient/notification/user.go | 4 +- pkg/rpcclient/push.go | 8 +- pkg/rpcclient/third.go | 48 ++- pkg/rpcclient/user.go | 13 +- tools/component/component.go | 98 ++++--- tools/component/component_test.go | 29 +- tools/up35/pkg/pkg.go | 34 ++- 114 files changed, 1735 insertions(+), 1794 deletions(-) delete mode 100644 pkg/common/db/s3/aws/aws.go mode change 100644 => 100755 pkg/common/tls/tls.go mode change 100644 => 100755 pkg/rpcclient/third.go diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 0fb609aa3..9a307686d 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -15,115 +15,17 @@ package main import ( - "context" - "fmt" - "net" - "net/http" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" _ "net/http/pprof" - "os" - "os/signal" - "strconv" - "syscall" - "time" - "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { apiCmd := cmd.NewApiCmd() apiCmd.AddPortFlag() apiCmd.AddPrometheusPortFlag() - apiCmd.AddApi(run) if err := apiCmd.Execute(); err != nil { util.ExitWithError(err) } } - -func run(port int, proPort int) error { - if port == 0 || proPort == 0 { - err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort) - return errs.Wrap(fmt.Errorf(err)) - } - rdb, err := cache.NewRedis() - if err != nil { - return err - } - - var client discoveryregistry.SvcDiscoveryRegistry - - // Determine whether zk is passed according to whether it is a clustered deployment - client, err = kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) - if err != nil { - return err - } - - if err = client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { - return err - } - - if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.Config.EncodeConfig()); err != nil { - return err - } - - var ( - netDone = make(chan struct{}, 1) - netErr error - ) - - router := api.NewGinRouter(client, rdb) - if config.Config.Prometheus.Enable { - go func() { - p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api")) - p.SetListenAddress(fmt.Sprintf(":%d", proPort)) - if err = p.Use(router); err != nil && err != http.ErrServerClosed { - netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", proPort)) - netDone <- struct{}{} - } - }() - } - - var address string - if config.Config.Api.ListenIP != "" { - address = net.JoinHostPort(config.Config.Api.ListenIP, strconv.Itoa(port)) - } else { - address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) - } - - server := http.Server{Addr: address, Handler: router} - - go func() { - err = server.ListenAndServe() - if err != nil && err != http.ErrServerClosed { - netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr)) - netDone <- struct{}{} - } - }() - - sigs := make(chan os.Signal, 1) - signal.Notify(sigs, syscall.SIGTERM) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - select { - case <-sigs: - util.SIGTERMExit() - err := server.Shutdown(ctx) - if err != nil { - return errs.Wrap(err, "api shutdown err") - } - case <-netDone: - close(netDone) - return netErr - } - return nil -} diff --git a/cmd/openim-crontask/main.go b/cmd/openim-crontask/main.go index b284fd773..b52029c64 100644 --- a/cmd/openim-crontask/main.go +++ b/cmd/openim-crontask/main.go @@ -15,14 +15,13 @@ package main import ( - "github.com/openimsdk/open-im-server/v3/internal/tools" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { cronTaskCmd := cmd.NewCronTaskCmd() - if err := cronTaskCmd.Exec(tools.StartTask); err != nil { + if err := cronTaskCmd.Exec(); err != nil { util.ExitWithError(err) } } diff --git a/cmd/openim-msggateway/main.go b/cmd/openim-msggateway/main.go index ed67b8f5d..01b13560d 100644 --- a/cmd/openim-msggateway/main.go +++ b/cmd/openim-msggateway/main.go @@ -24,7 +24,6 @@ func main() { msgGatewayCmd.AddWsPortFlag() msgGatewayCmd.AddPortFlag() msgGatewayCmd.AddPrometheusPortFlag() - if err := msgGatewayCmd.Exec(); err != nil { util.ExitWithError(err) } diff --git a/cmd/openim-push/main.go b/cmd/openim-push/main.go index bd31ffdef..c7d29fc97 100644 --- a/cmd/openim-push/main.go +++ b/cmd/openim-push/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/push" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - pushCmd := cmd.NewRpcCmd(cmd.RpcPushServer) + pushCmd := cmd.NewRpcCmd(cmd.RpcPushServer, push.Start) pushCmd.AddPortFlag() pushCmd.AddPrometheusPortFlag() if err := pushCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := pushCmd.StartSvr(config.Config.RpcRegisterName.OpenImPushName, push.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-auth/main.go b/cmd/openim-rpc/openim-rpc-auth/main.go index 992a2b432..da281b70e 100644 --- a/cmd/openim-rpc/openim-rpc-auth/main.go +++ b/cmd/openim-rpc/openim-rpc-auth/main.go @@ -17,19 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/auth" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - authCmd := cmd.NewRpcCmd(cmd.RpcAuthServer) + authCmd := cmd.NewRpcCmd(cmd.RpcAuthServer, auth.Start) authCmd.AddPortFlag() authCmd.AddPrometheusPortFlag() if err := authCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := authCmd.StartSvr(config.Config.RpcRegisterName.OpenImAuthName, auth.Start); err != nil { - util.ExitWithError(err) - } - } diff --git a/cmd/openim-rpc/openim-rpc-conversation/main.go b/cmd/openim-rpc/openim-rpc-conversation/main.go index 10fe0b46c..6e74b3251 100644 --- a/cmd/openim-rpc/openim-rpc-conversation/main.go +++ b/cmd/openim-rpc/openim-rpc-conversation/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/conversation" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcConversationServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcConversationServer, conversation.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImConversationName, conversation.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-friend/main.go b/cmd/openim-rpc/openim-rpc-friend/main.go index 63de23293..a307c01a1 100644 --- a/cmd/openim-rpc/openim-rpc-friend/main.go +++ b/cmd/openim-rpc/openim-rpc-friend/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/friend" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcFriendServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcFriendServer, friend.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImFriendName, friend.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-group/main.go b/cmd/openim-rpc/openim-rpc-group/main.go index c0780acab..2afb7963c 100644 --- a/cmd/openim-rpc/openim-rpc-group/main.go +++ b/cmd/openim-rpc/openim-rpc-group/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/group" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcGroupServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcGroupServer, group.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImGroupName, group.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-msg/main.go b/cmd/openim-rpc/openim-rpc-msg/main.go index 62bdff0a5..bbffbcae7 100644 --- a/cmd/openim-rpc/openim-rpc-msg/main.go +++ b/cmd/openim-rpc/openim-rpc-msg/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/msg" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcMsgServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcMsgServer, msg.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImMsgName, msg.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-third/main.go b/cmd/openim-rpc/openim-rpc-third/main.go index c2893a398..09a8409e6 100644 --- a/cmd/openim-rpc/openim-rpc-third/main.go +++ b/cmd/openim-rpc/openim-rpc-third/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/third" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcThirdServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcThirdServer, third.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImThirdName, third.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/cmd/openim-rpc/openim-rpc-user/main.go b/cmd/openim-rpc/openim-rpc-user/main.go index f7948bda0..18adbfae5 100644 --- a/cmd/openim-rpc/openim-rpc-user/main.go +++ b/cmd/openim-rpc/openim-rpc-user/main.go @@ -17,18 +17,14 @@ package main import ( "github.com/openimsdk/open-im-server/v3/internal/rpc/user" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { - rpcCmd := cmd.NewRpcCmd(cmd.RpcUserServer) + rpcCmd := cmd.NewRpcCmd(cmd.RpcUserServer, user.Start) rpcCmd.AddPortFlag() rpcCmd.AddPrometheusPortFlag() if err := rpcCmd.Exec(); err != nil { util.ExitWithError(err) } - if err := rpcCmd.StartSvr(config.Config.RpcRegisterName.OpenImUserName, user.Start); err != nil { - util.ExitWithError(err) - } } diff --git a/internal/api/msg.go b/internal/api/msg.go index 61cd1ae6c..d38c14d4e 100644 --- a/internal/api/msg.go +++ b/internal/api/msg.go @@ -198,7 +198,7 @@ func (m *MessageApi) SendMessage(c *gin.Context) { } // Check if the user has the app manager role. - if !authverify.IsAppManagerUid(c) { + if !authverify.IsAppManagerUid(c, m.Config) { // Respond with a permission error if the user is not an app manager. apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) return @@ -256,7 +256,7 @@ func (m *MessageApi) SendBusinessNotification(c *gin.Context) { return } - if !authverify.IsAppManagerUid(c) { + if !authverify.IsAppManagerUid(c, m.Config) { apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) return } @@ -300,7 +300,7 @@ func (m *MessageApi) BatchSendMsg(c *gin.Context) { return } log.ZInfo(c, "BatchSendMsg", "req", req) - if err := authverify.CheckAdmin(c); err != nil { + if err := authverify.CheckAdmin(c, m.Config); err != nil { apiresp.GinError(c, errs.ErrNoPermission.Wrap("only app manager can send message")) return } diff --git a/internal/api/route.go b/internal/api/route.go index 5c920ca05..ce8e3a62b 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -17,53 +17,142 @@ package api import ( "context" "fmt" + kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" + ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "net" "net/http" + "os" + "os/signal" + "strconv" + "syscall" + "time" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/apiresp" - "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/tokenverify" - "github.com/gin-gonic/gin" - "github.com/gin-gonic/gin/binding" - "github.com/go-playground/validator/v10" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + + "github.com/gin-gonic/gin" + "github.com/gin-gonic/gin/binding" + "github.com/go-playground/validator/v10" "github.com/redis/go-redis/v9" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mw" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) -func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient) *gin.Engine { - discov.AddOption( - mw.GrpcClient(), - grpc.WithTransportCredentials(insecure.NewCredentials()), - grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin")), - ) // Default RPC middleware +func Start(config *config.GlobalConfig, port int, proPort int) error { + log.ZDebug(context.Background(), "configAPI1111111111111111111", config, "port", port, "javafdasfs") + if port == 0 || proPort == 0 { + err := "port or proPort is empty:" + strconv.Itoa(port) + "," + strconv.Itoa(proPort) + return errs.Wrap(fmt.Errorf(err)) + } + rdb, err := cache.NewRedis(config) + if err != nil { + return err + } + + var client discoveryregistry.SvcDiscoveryRegistry + + // Determine whether zk is passed according to whether it is a clustered deployment + client, err = kdisc.NewDiscoveryRegister(config) + if err != nil { + return errs.Wrap(err, "register discovery err") + } + + if err = client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { + return errs.Wrap(err, "create rpc root nodes error") + } + + if err = client.RegisterConf2Registry(constant.OpenIMCommonConfigKey, config.EncodeConfig()); err != nil { + return errs.Wrap(err) + } + var ( + netDone = make(chan struct{}, 1) + netErr error + ) + router := newGinRouter(client, rdb, config) + if config.Prometheus.Enable { + go func() { + p := ginprom.NewPrometheus("app", prommetrics.GetGinCusMetrics("Api")) + p.SetListenAddress(fmt.Sprintf(":%d", proPort)) + if err = p.Use(router); err != nil && err != http.ErrServerClosed { + netErr = errs.Wrap(err, fmt.Sprintf("prometheus start err: %d", proPort)) + netDone <- struct{}{} + } + }() + + } + + var address string + if config.Api.ListenIP != "" { + address = net.JoinHostPort(config.Api.ListenIP, strconv.Itoa(port)) + } else { + address = net.JoinHostPort("0.0.0.0", strconv.Itoa(port)) + } + + server := http.Server{Addr: address, Handler: router} + + go func() { + err = server.ListenAndServe() + if err != nil && err != http.ErrServerClosed { + netErr = errs.Wrap(err, fmt.Sprintf("api start err: %s", server.Addr)) + netDone <- struct{}{} + + } + }() + + sigs := make(chan os.Signal, 1) + signal.Notify(sigs, syscall.SIGTERM) + + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) + defer cancel() + select { + case <-sigs: + util.SIGTERMExit() + err := server.Shutdown(ctx) + if err != nil { + return errs.Wrap(err, "shutdown err") + } + case <-netDone: + close(netDone) + return netErr + } + return nil +} + +func newGinRouter(disCov discoveryregistry.SvcDiscoveryRegistry, rdb redis.UniversalClient, config *config.GlobalConfig) *gin.Engine { + disCov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) gin.SetMode(gin.ReleaseMode) r := gin.New() if v, ok := binding.Validator.Engine().(*validator.Validate); ok { _ = v.RegisterValidation("required_if", RequiredIf) } - log.ZInfo(context.Background(), "load config", "config", config.Config) r.Use(gin.Recovery(), mw.CorsHandler(), mw.GinParseOperationID()) // init rpc client here - userRpc := rpcclient.NewUser(discov) - groupRpc := rpcclient.NewGroup(discov) - friendRpc := rpcclient.NewFriend(discov) - messageRpc := rpcclient.NewMessage(discov) - conversationRpc := rpcclient.NewConversation(discov) - authRpc := rpcclient.NewAuth(discov) - thirdRpc := rpcclient.NewThird(discov) + userRpc := rpcclient.NewUser(disCov, config) + groupRpc := rpcclient.NewGroup(disCov, config) + friendRpc := rpcclient.NewFriend(disCov, config) + messageRpc := rpcclient.NewMessage(disCov, config) + conversationRpc := rpcclient.NewConversation(disCov, config) + authRpc := rpcclient.NewAuth(disCov, config) + thirdRpc := rpcclient.NewThird(disCov, config) u := NewUserApi(*userRpc) m := NewMessageApi(messageRpc, userRpc) - ParseToken := GinParseToken(rdb) + ParseToken := GinParseToken(rdb, config) userRouterGroup := r.Group("/user") { userRouterGroup.POST("/user_register", u.UserRegister) @@ -157,8 +246,8 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive // Third service thirdGroup := r.Group("/third", ParseToken) { - thirdGroup.GET("/prometheus", GetPrometheus) t := NewThirdApi(*thirdRpc) + thirdGroup.GET("/prometheus", t.GetPrometheus) thirdGroup.POST("/fcm_update_token", t.FcmUpdateToken) thirdGroup.POST("/set_app_badge", t.SetAppBadge) @@ -225,12 +314,12 @@ func NewGinRouter(discov discoveryregistry.SvcDiscoveryRegistry, rdb redis.Unive return r } -// GinParseToken is a middleware that parses the token in the request header and verifies it. -func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { +func GinParseToken(rdb redis.UniversalClient, config *config.GlobalConfig) gin.HandlerFunc { dataBase := controller.NewAuthDatabase( - cache.NewMsgCacheModel(rdb), - config.Config.Secret, - config.Config.TokenPolicy.Expire, + cache.NewMsgCacheModel(rdb, config), + config.Secret, + config.TokenPolicy.Expire, + config, ) return func(c *gin.Context) { switch c.Request.Method { @@ -242,7 +331,7 @@ func GinParseToken(rdb redis.UniversalClient) gin.HandlerFunc { c.Abort() return } - claims, err := tokenverify.GetClaimFromToken(token, authverify.Secret()) + claims, err := tokenverify.GetClaimFromToken(token, authverify.Secret(config.Secret)) if err != nil { log.ZWarn(c, "jwt get token error", errs.ErrTokenUnknown.Wrap()) apiresp.GinError(c, errs.ErrTokenUnknown.Wrap()) diff --git a/internal/api/third.go b/internal/api/third.go index f00c0d8d3..190e0d540 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -19,12 +19,12 @@ import ( "net/http" "strconv" + "github.com/gin-gonic/gin" + "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/a2r" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" - "github.com/gin-gonic/gin" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) @@ -126,6 +126,6 @@ func (o *ThirdApi) SearchLogs(c *gin.Context) { a2r.Call(third.ThirdClient.SearchLogs, o.Client, c) } -func GetPrometheus(c *gin.Context) { - c.Redirect(http.StatusFound, config2.Config.Prometheus.GrafanaUrl) +func (o *ThirdApi) GetPrometheus(c *gin.Context) { + c.Redirect(http.StatusFound, o.Config.Prometheus.GrafanaUrl) } diff --git a/internal/api/user.go b/internal/api/user.go index 3cc5470a7..468432ee0 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -23,7 +23,7 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) @@ -70,7 +70,7 @@ func (u *UserApi) GetUsersOnlineStatus(c *gin.Context) { apiresp.GinError(c, err) return } - conns, err := u.Discov.GetConns(c, config.Config.RpcRegisterName.OpenImMessageGatewayName) + conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName) if err != nil { apiresp.GinError(c, err) return @@ -134,7 +134,7 @@ func (u *UserApi) GetUsersOnlineTokenDetail(c *gin.Context) { apiresp.GinError(c, errs.ErrArgs.WithDetail(err.Error()).Wrap()) return } - conns, err := u.Discov.GetConns(c, config.Config.RpcRegisterName.OpenImMessageGatewayName) + conns, err := u.Discov.GetConns(c, u.Config.RpcRegisterName.OpenImMessageGatewayName) if err != nil { apiresp.GinError(c, err) return diff --git a/internal/msggateway/callback.go b/internal/msggateway/callback.go index ede48f74a..ab8c1f51f 100644 --- a/internal/msggateway/callback.go +++ b/internal/msggateway/callback.go @@ -25,12 +25,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -func callBackURL() string { - return config.Config.Callback.CallbackUrl -} - -func CallbackUserOnline(ctx context.Context, userID string, platformID int, isAppBackground bool, connID string) error { - if !config.Config.Callback.CallbackUserOnline.Enable { +func CallbackUserOnline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, isAppBackground bool, connID string) error { + if !globalConfig.Callback.CallbackUserOnline.Enable { return nil } req := cbapi.CallbackUserOnlineReq{ @@ -48,14 +44,14 @@ func CallbackUserOnline(ctx context.Context, userID string, platformID int, isAp ConnID: connID, } resp := cbapi.CommonCallbackResp{} - if err := http.CallBackPostReturn(ctx, callBackURL(), &req, &resp, config.Config.Callback.CallbackUserOnline); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, &req, &resp, globalConfig.Callback.CallbackUserOnline); err != nil { return err } return nil } -func CallbackUserOffline(ctx context.Context, userID string, platformID int, connID string) error { - if !config.Config.Callback.CallbackUserOffline.Enable { +func CallbackUserOffline(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int, connID string) error { + if !globalConfig.Callback.CallbackUserOffline.Enable { return nil } req := &cbapi.CallbackUserOfflineReq{ @@ -72,14 +68,14 @@ func CallbackUserOffline(ctx context.Context, userID string, platformID int, con ConnID: connID, } resp := &cbapi.CallbackUserOfflineResp{} - if err := http.CallBackPostReturn(ctx, callBackURL(), req, resp, config.Config.Callback.CallbackUserOffline); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil { return err } return nil } -func CallbackUserKickOff(ctx context.Context, userID string, platformID int) error { - if !config.Config.Callback.CallbackUserKickOff.Enable { +func CallbackUserKickOff(ctx context.Context, globalConfig *config.GlobalConfig, userID string, platformID int) error { + if !globalConfig.Callback.CallbackUserKickOff.Enable { return nil } req := &cbapi.CallbackUserKickOffReq{ @@ -95,7 +91,7 @@ func CallbackUserKickOff(ctx context.Context, userID string, platformID int) err Seq: time.Now().UnixMilli(), } resp := &cbapi.CommonCallbackResp{} - if err := http.CallBackPostReturn(ctx, callBackURL(), req, resp, config.Config.Callback.CallbackUserOffline); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackUserOffline); err != nil { return err } return nil diff --git a/internal/msggateway/hub_server.go b/internal/msggateway/hub_server.go index 826c2488b..739c4232f 100644 --- a/internal/msggateway/hub_server.go +++ b/internal/msggateway/hub_server.go @@ -31,24 +31,25 @@ import ( "google.golang.org/grpc" ) -func (s *Server) InitServer(disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func (s *Server) InitServer(config *config.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - msgModel := cache.NewMsgCacheModel(rdb) - s.LongConnServer.SetDiscoveryRegistry(disCov) + msgModel := cache.NewMsgCacheModel(rdb, config) + s.LongConnServer.SetDiscoveryRegistry(disCov, config) s.LongConnServer.SetCacheHandler(msgModel) msggateway.RegisterMsgGatewayServer(server, s) return nil } -func (s *Server) Start() error { +func (s *Server) Start(conf *config.GlobalConfig) error { return startrpc.Start( s.rpcPort, - config.Config.RpcRegisterName.OpenImMessageGatewayName, + conf.RpcRegisterName.OpenImMessageGatewayName, s.prometheusPort, + conf, s.InitServer, ) } @@ -58,18 +59,20 @@ type Server struct { prometheusPort int LongConnServer LongConnServer pushTerminal []int + config *config.GlobalConfig } func (s *Server) SetLongConnServer(LongConnServer LongConnServer) { s.LongConnServer = LongConnServer } -func NewServer(rpcPort int, proPort int, longConnServer LongConnServer) *Server { +func NewServer(rpcPort int, proPort int, longConnServer LongConnServer, config *config.GlobalConfig) *Server { return &Server{ rpcPort: rpcPort, prometheusPort: proPort, LongConnServer: longConnServer, pushTerminal: []int{constant.IOSPlatformID, constant.AndroidPlatformID}, + config: config, } } @@ -84,7 +87,7 @@ func (s *Server) GetUsersOnlineStatus( ctx context.Context, req *msggateway.GetUsersOnlineStatusReq, ) (*msggateway.GetUsersOnlineStatusResp, error) { - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { return nil, errs.ErrNoPermission.Wrap("only app manager") } var resp msggateway.GetUsersOnlineStatusResp diff --git a/internal/msggateway/init.go b/internal/msggateway/init.go index 5d19ad16d..4efbb7cdf 100644 --- a/internal/msggateway/init.go +++ b/internal/msggateway/init.go @@ -22,23 +22,24 @@ import ( ) // RunWsAndServer run ws server. -func RunWsAndServer(rpcPort, wsPort, prometheusPort int) error { +func RunWsAndServer(conf *config.GlobalConfig, rpcPort, wsPort, prometheusPort int) error { fmt.Println("start rpc/msg_gateway server, port: ", rpcPort, wsPort, prometheusPort, ", OpenIM version: ", config.Version) longServer, err := NewWsServer( + conf, WithPort(wsPort), - WithMaxConnNum(int64(config.Config.LongConnSvr.WebsocketMaxConnNum)), - WithHandshakeTimeout(time.Duration(config.Config.LongConnSvr.WebsocketTimeout)*time.Second), - WithMessageMaxMsgLength(config.Config.LongConnSvr.WebsocketMaxMsgLen), - WithWriteBufferSize(config.Config.LongConnSvr.WebsocketWriteBufferSize), + WithMaxConnNum(int64(conf.LongConnSvr.WebsocketMaxConnNum)), + WithHandshakeTimeout(time.Duration(conf.LongConnSvr.WebsocketTimeout)*time.Second), + WithMessageMaxMsgLength(conf.LongConnSvr.WebsocketMaxMsgLen), + WithWriteBufferSize(conf.LongConnSvr.WebsocketWriteBufferSize), ) if err != nil { return err } - hubServer := NewServer(rpcPort, prometheusPort, longServer) + hubServer := NewServer(rpcPort, prometheusPort, longServer, conf) netDone := make(chan error) go func() { - err = hubServer.Start() + err = hubServer.Start(conf) netDone <- err }() return hubServer.LongConnServer.Run(netDone) diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 105a77336..208cd6bf7 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -16,6 +16,7 @@ package msggateway import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "sync" "github.com/OpenIMSDK/protocol/msg" @@ -105,9 +106,9 @@ type GrpcHandler struct { validate *validator.Validate } -func NewGrpcHandler(validate *validator.Validate, client discoveryregistry.SvcDiscoveryRegistry) *GrpcHandler { - msgRpcClient := rpcclient.NewMessageRpcClient(client) - pushRpcClient := rpcclient.NewPushRpcClient(client) +func NewGrpcHandler(validate *validator.Validate, client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *GrpcHandler { + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) + pushRpcClient := rpcclient.NewPushRpcClient(client, config) return &GrpcHandler{ msgRpcClient: &msgRpcClient, pushClient: &pushRpcClient, validate: validate, diff --git a/internal/msggateway/n_ws_server.go b/internal/msggateway/n_ws_server.go index e8ba9939a..f5838c703 100644 --- a/internal/msggateway/n_ws_server.go +++ b/internal/msggateway/n_ws_server.go @@ -49,7 +49,7 @@ type LongConnServer interface { GetUserPlatformCons(userID string, platform int) ([]*Client, bool, bool) Validate(s any) error SetCacheHandler(cache cache.MsgModel) - SetDiscoveryRegistry(client discoveryregistry.SvcDiscoveryRegistry) + SetDiscoveryRegistry(client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) KickUserConn(client *Client) error UnRegister(c *Client) SetKickHandlerInfo(i *kickHandler) @@ -66,6 +66,7 @@ type LongConnServer interface { // } type WsServer struct { + globalConfig *config.GlobalConfig port int wsMaxConnNum int64 registerChan chan *Client @@ -92,9 +93,9 @@ type kickHandler struct { newClient *Client } -func (ws *WsServer) SetDiscoveryRegistry(disCov discoveryregistry.SvcDiscoveryRegistry) { - ws.MessageHandler = NewGrpcHandler(ws.validate, disCov) - u := rpcclient.NewUserRpcClient(disCov) +func (ws *WsServer) SetDiscoveryRegistry(disCov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) { + ws.MessageHandler = NewGrpcHandler(ws.validate, disCov, config) + u := rpcclient.NewUserRpcClient(disCov, config) ws.userClient = &u ws.disCov = disCov } @@ -106,12 +107,12 @@ func (ws *WsServer) SetUserOnlineStatus(ctx context.Context, client *Client, sta } switch status { case constant.Online: - err := CallbackUserOnline(ctx, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID()) + err := CallbackUserOnline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.IsBackground, client.ctx.GetConnID()) if err != nil { log.ZWarn(ctx, "CallbackUserOnline err", err) } case constant.Offline: - err := CallbackUserOffline(ctx, client.UserID, client.PlatformID, client.ctx.GetConnID()) + err := CallbackUserOffline(ctx, ws.globalConfig, client.UserID, client.PlatformID, client.ctx.GetConnID()) if err != nil { log.ZWarn(ctx, "CallbackUserOffline err", err) } @@ -141,13 +142,14 @@ func (ws *WsServer) GetUserPlatformCons(userID string, platform int) ([]*Client, return ws.clients.Get(userID, platform) } -func NewWsServer(opts ...Option) (*WsServer, error) { +func NewWsServer(globalConfig *config.GlobalConfig, opts ...Option) (*WsServer, error) { var config configs for _, o := range opts { o(&config) } v := validator.New() return &WsServer{ + globalConfig: globalConfig, port: config.port, wsMaxConnNum: config.maxConnNum, writeBufferSize: config.writeBufferSize, @@ -221,7 +223,7 @@ func (ws *WsServer) Run(done chan error) error { var concurrentRequest = 3 func (ws *WsServer) sendUserOnlineInfoToOtherNode(ctx context.Context, client *Client) error { - conns, err := ws.disCov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) + conns, err := ws.disCov.GetConns(ctx, ws.globalConfig.RpcRegisterName.OpenImMessageGatewayName) if err != nil { return err } @@ -286,7 +288,7 @@ func (ws *WsServer) registerClient(client *Client) { } wg := sync.WaitGroup{} - if config.Config.Envs.Discovery == "zookeeper" { + if ws.globalConfig.Envs.Discovery == "zookeeper" { wg.Add(1) go func() { defer wg.Done() @@ -329,7 +331,7 @@ func (ws *WsServer) KickUserConn(client *Client) error { } func (ws *WsServer) multiTerminalLoginChecker(clientOK bool, oldClients []*Client, newClient *Client) { - switch config.Config.MultiLoginPolicy { + switch ws.globalConfig.MultiLoginPolicy { case constant.DefalutNotKick: case constant.PCAndOther: if constant.PlatformIDToClass(newClient.PlatformID) == constant.TerminalPC { @@ -441,7 +443,7 @@ func (ws *WsServer) ParseWSArgs(r *http.Request) (args *WSArgs, err error) { return nil, errs.ErrConnArgsErr.Wrap("platformID is not int") } v.PlatformID = platformID - if err = authverify.WsVerifyToken(v.Token, v.UserID, platformID); err != nil { + if err = authverify.WsVerifyToken(v.Token, v.UserID, ws.globalConfig.Secret, platformID); err != nil { return nil, err } if query.Get(Compression) == GzipCompressionProtocol { diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index dfb7c5307..5e9e80663 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -44,21 +44,22 @@ type MsgTransfer struct { // This consumer aggregated messages, subscribed to the topic:ws2ms_chat, // the modification notification is sent to msg_to_modify topic, the message is stored in redis, Incr Redis, // and then the message is sent to ms2pschat topic for push, and the message is sent to msg_to_mongo topic for persistence - historyCH *OnlineHistoryRedisConsumerHandler + historyCH *OnlineHistoryRedisConsumerHandler + historyMongoCH *OnlineHistoryMongoConsumerHandler // mongoDB batch insert, delete messages in redis after success, // and handle the deletion notification message deleted subscriptions topic: msg_to_mongo - historyMongoCH *OnlineHistoryMongoConsumerHandler - ctx context.Context - cancel context.CancelFunc + ctx context.Context + cancel context.CancelFunc + config *config.GlobalConfig } -func StartTransfer(prometheusPort int) error { - rdb, err := cache.NewRedis() +func StartTransfer(config *config.GlobalConfig, prometheusPort int) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return err } @@ -66,38 +67,37 @@ func StartTransfer(prometheusPort int) error { if err = mongo.CreateMsgIndex(); err != nil { return err } - - client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) + client, err := kdisc.NewDiscoveryRegister(config) if err != nil { return err } - if err := client.CreateRpcRootNodes(config.Config.GetServiceNames()); err != nil { + if err := client.CreateRpcRootNodes(config.GetServiceNames()); err != nil { return err } client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) - msgModel := cache.NewMsgCacheModel(rdb) - msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) - msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel) + msgModel := cache.NewMsgCacheModel(rdb, config) + msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database)) + msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, msgModel, config) if err != nil { return err } - conversationRpcClient := rpcclient.NewConversationRpcClient(client) - groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgTransfer, err := NewMsgTransfer(msgDatabase, &conversationRpcClient, &groupRpcClient) + conversationRpcClient := rpcclient.NewConversationRpcClient(client, config) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + msgTransfer, err := NewMsgTransfer(config, msgDatabase, &conversationRpcClient, &groupRpcClient) if err != nil { return err } - return msgTransfer.Start(prometheusPort) + return msgTransfer.Start(prometheusPort, config) } -func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) { - historyCH, err := NewOnlineHistoryRedisConsumerHandler(msgDatabase, conversationRpcClient, groupRpcClient) +func NewMsgTransfer(config *config.GlobalConfig, msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) { + historyCH, err := NewOnlineHistoryRedisConsumerHandler(config, msgDatabase, conversationRpcClient, groupRpcClient) if err != nil { return nil, err } - historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(msgDatabase) + historyMongoCH, err := NewOnlineHistoryMongoConsumerHandler(config, msgDatabase) if err != nil { return nil, err } @@ -105,11 +105,12 @@ func NewMsgTransfer(msgDatabase controller.CommonMsgDatabase, conversationRpcCli return &MsgTransfer{ historyCH: historyCH, historyMongoCH: historyMongoCH, + config: config, }, nil } -func (m *MsgTransfer) Start(prometheusPort int) error { - fmt.Println("Start msg transfer", "prometheusPort:", prometheusPort) +func (m *MsgTransfer) Start(prometheusPort int, config *config.GlobalConfig) error { + fmt.Println("start msg transfer", "prometheusPort:", prometheusPort) if prometheusPort <= 0 { return errs.Wrap(errors.New("prometheusPort not correct")) } @@ -123,13 +124,13 @@ func (m *MsgTransfer) Start(prometheusPort int) error { go m.historyCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyCH) go m.historyMongoCH.historyConsumerGroup.RegisterHandleAndConsumer(m.ctx, m.historyMongoCH) - if config.Config.Prometheus.Enable { + if config.Prometheus.Enable { go func() { proreg := prometheus.NewRegistry() proreg.MustRegister( collectors.NewGoCollector(), ) - proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer")...) + proreg.MustRegister(prommetrics.GetGrpcCusMetrics("Transfer", config)...) http.Handle("/metrics", promhttp.HandlerFor(proreg, promhttp.HandlerOpts{Registry: proreg})) err := http.ListenAndServe(fmt.Sprintf(":%d", prometheusPort), nil) if err != nil && err != http.ErrServerClosed { diff --git a/internal/msgtransfer/online_history_msg_handler.go b/internal/msgtransfer/online_history_msg_handler.go index 4995d10e8..b81bd12b8 100644 --- a/internal/msgtransfer/online_history_msg_handler.go +++ b/internal/msgtransfer/online_history_msg_handler.go @@ -81,6 +81,7 @@ type OnlineHistoryRedisConsumerHandler struct { } func NewOnlineHistoryRedisConsumerHandler( + config *config.GlobalConfig, database controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, @@ -96,11 +97,29 @@ func NewOnlineHistoryRedisConsumerHandler( och.conversationRpcClient = conversationRpcClient och.groupRpcClient = groupRpcClient var err error + + var tlsConfig *kafka.TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &kafka.TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: false, + } + } + och.historyConsumerGroup, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.LatestMsgToRedis.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis) + OffsetsInitial: sarama.OffsetNewest, + IsReturnErr: false, + UserName: config.Kafka.Username, + Password: config.Kafka.Password, + }, []string{config.Kafka.LatestMsgToRedis.Topic}, + config.Kafka.Addr, + config.Kafka.ConsumerGroupID.MsgToRedis, + tlsConfig, + ) // statistics.NewStatistics(&och.singleMsgSuccessCount, config.Config.ModuleName.MsgTransferName, fmt.Sprintf("%d // second singleMsgCount insert to mongo", constant.StatisticsTimeInterval), constant.StatisticsTimeInterval) return &och, err diff --git a/internal/msgtransfer/online_msg_to_mongo_handler.go b/internal/msgtransfer/online_msg_to_mongo_handler.go index efffc191f..045f82220 100644 --- a/internal/msgtransfer/online_msg_to_mongo_handler.go +++ b/internal/msgtransfer/online_msg_to_mongo_handler.go @@ -32,12 +32,28 @@ type OnlineHistoryMongoConsumerHandler struct { msgDatabase controller.CommonMsgDatabase } -func NewOnlineHistoryMongoConsumerHandler(database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) { +func NewOnlineHistoryMongoConsumerHandler(config *config.GlobalConfig, database controller.CommonMsgDatabase) (*OnlineHistoryMongoConsumerHandler, error) { + var tlsConfig *kfk.TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &kfk.TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: false, + } + } historyConsumerGroup, err := kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.MsgToMongo.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo) + OffsetsInitial: sarama.OffsetNewest, + IsReturnErr: false, + UserName: config.Kafka.Username, + Password: config.Kafka.Password, + }, []string{config.Kafka.MsgToMongo.Topic}, + config.Kafka.Addr, + config.Kafka.ConsumerGroupID.MsgToMongo, + tlsConfig, + ) if err != nil { return nil, err } diff --git a/internal/push/callback.go b/internal/push/callback.go index 70862e4d2..6415d63d6 100644 --- a/internal/push/callback.go +++ b/internal/push/callback.go @@ -26,12 +26,14 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -func url() string { - return config.Config.Callback.CallbackUrl -} - -func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData, offlinePushUserIDs *[]string) error { - if !config.Config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing { +func callbackOfflinePush( + ctx context.Context, + config *config.GlobalConfig, + userIDs []string, + msg *sdkws.MsgData, + offlinePushUserIDs *[]string, +) error { + if !config.Callback.CallbackOfflinePush.Enable || msg.ContentType == constant.Typing { return nil } req := &callbackstruct.CallbackBeforePushReq{ @@ -55,7 +57,7 @@ func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgDa } resp := &callbackstruct.CallbackBeforePushResp{} - if err := http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOfflinePush); err != nil { + if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOfflinePush); err != nil { return err } @@ -68,8 +70,8 @@ func callbackOfflinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgDa return nil } -func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { - if !config.Config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing { +func callbackOnlinePush(ctx context.Context, config *config.GlobalConfig, userIDs []string, msg *sdkws.MsgData) error { + if !config.Callback.CallbackOnlinePush.Enable || utils.Contain(msg.SendID, userIDs...) || msg.ContentType == constant.Typing { return nil } req := callbackstruct.CallbackBeforePushReq{ @@ -91,7 +93,7 @@ func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgDat Content: GetContent(msg), } resp := &callbackstruct.CallbackBeforePushResp{} - if err := http.CallBackPostReturn(ctx, url(), req, resp, config.Config.Callback.CallbackOnlinePush); err != nil { + if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackOnlinePush); err != nil { return err } return nil @@ -99,11 +101,12 @@ func callbackOnlinePush(ctx context.Context, userIDs []string, msg *sdkws.MsgDat func callbackBeforeSuperGroupOnlinePush( ctx context.Context, + config *config.GlobalConfig, groupID string, msg *sdkws.MsgData, pushToUserIDs *[]string, ) error { - if !config.Config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing { + if !config.Callback.CallbackBeforeSuperGroupOnlinePush.Enable || msg.ContentType == constant.Typing { return nil } req := callbackstruct.CallbackBeforeSuperGroupOnlinePushReq{ @@ -123,7 +126,7 @@ func callbackBeforeSuperGroupOnlinePush( Seq: msg.Seq, } resp := &callbackstruct.CallbackBeforeSuperGroupOnlinePushResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, req, resp, config.Config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil { + if err := http.CallBackPostReturn(ctx, config.Callback.CallbackUrl, req, resp, config.Callback.CallbackBeforeSuperGroupOnlinePush); err != nil { return err } diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index 92ce4714e..4ad77de2c 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -16,6 +16,7 @@ package push import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type Consumer struct { @@ -24,8 +25,8 @@ type Consumer struct { // successCount uint64 } -func NewConsumer(pusher *Pusher) (*Consumer, error) { - c, err := NewConsumerHandler(pusher) +func NewConsumer(config *config.GlobalConfig, pusher *Pusher) (*Consumer, error) { + c, err := NewConsumerHandler(config, pusher) if err != nil { return nil, err } @@ -36,5 +37,4 @@ func NewConsumer(pusher *Pusher) (*Consumer, error) { func (c *Consumer) Start() { go c.pushCh.pushConsumerGroup.RegisterHandleAndConsumer(context.Background(), &c.pushCh) - } diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index aa6ec186f..977254462 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -20,12 +20,14 @@ import ( firebase "firebase.google.com/go" "firebase.google.com/go/messaging" + "github.com/redis/go-redis/v9" + "google.golang.org/api/option" + "github.com/OpenIMSDK/protocol/constant" + "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/redis/go-redis/v9" - "google.golang.org/api/option" ) const SinglePushCountLimit = 400 @@ -39,9 +41,9 @@ type Fcm struct { // NewClient initializes a new FCM client using the Firebase Admin SDK. // It requires the FCM service account credentials file located within the project's configuration directory. -func NewClient(cache cache.MsgModel) *Fcm { - projectRoot, _ := config.GetProjectRoot() - credentialsFilePath := filepath.Join(projectRoot, "config", config.Config.Push.Fcm.ServiceAccount) +func NewClient(globalConfig *config.GlobalConfig, cache cache.MsgModel) *Fcm { + projectRoot := config.GetProjectRoot() + credentialsFilePath := filepath.Join(projectRoot, "config", globalConfig.Push.Fcm.ServiceAccount) opt := option.WithCredentialsFile(credentialsFilePath) fcmApp, err := firebase.NewApp(context.Background(), nil, opt) if err != nil { diff --git a/internal/push/offlinepush/getui/body.go b/internal/push/offlinepush/getui/body.go index 01eb22e73..46479163f 100644 --- a/internal/push/offlinepush/getui/body.go +++ b/internal/push/offlinepush/getui/body.go @@ -133,13 +133,13 @@ type Payload struct { IsSignal bool `json:"isSignal"` } -func newPushReq(title, content string) PushReq { +func newPushReq(config *config.GlobalConfig, title, content string) PushReq { pushReq := PushReq{PushMessage: &PushMessage{Notification: &Notification{ Title: title, Body: content, ClickType: "startapp", - ChannelID: config.Config.Push.GeTui.ChannelID, - ChannelName: config.Config.Push.GeTui.ChannelName, + ChannelID: config.Push.GeTui.ChannelID, + ChannelName: config.Push.GeTui.ChannelName, }}} return pushReq } diff --git a/internal/push/offlinepush/getui/push.go b/internal/push/offlinepush/getui/push.go index e950585a1..67f6292db 100644 --- a/internal/push/offlinepush/getui/push.go +++ b/internal/push/offlinepush/getui/push.go @@ -55,10 +55,15 @@ type Client struct { cache cache.MsgModel tokenExpireTime int64 taskIDTTL int64 + config *config.GlobalConfig } -func NewClient(cache cache.MsgModel) *Client { - return &Client{cache: cache, tokenExpireTime: tokenExpireTime, taskIDTTL: taskIDTTL} +func NewClient(config *config.GlobalConfig, cache cache.MsgModel) *Client { + return &Client{cache: cache, + tokenExpireTime: tokenExpireTime, + taskIDTTL: taskIDTTL, + config: config, + } } func (g *Client) Push(ctx context.Context, userIDs []string, title, content string, opts *offlinepush.Opts) error { @@ -74,7 +79,7 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri return err } } - pushReq := newPushReq(title, content) + pushReq := newPushReq(g.config, title, content) pushReq.setPushChannel(title, content) if len(userIDs) > 1 { maxNum := 999 @@ -85,9 +90,9 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri for i, v := range s.GetSplitResult() { go func(index int, userIDs []string) { defer wg.Done() - if err2 := g.batchPush(ctx, token, userIDs, pushReq); err2 != nil { - log.ZError(ctx, "batchPush failed", err2, "index", index, "token", token, "req", pushReq) - err = err2 + if err := g.batchPush(ctx, token, userIDs, pushReq); err != nil { + log.ZError(ctx, "batchPush failed", err, "index", index, "token", token, "req", pushReq) + err = err } }(i, v.Item) } @@ -110,13 +115,13 @@ func (g *Client) Push(ctx context.Context, userIDs []string, title, content stri func (g *Client) Auth(ctx context.Context, timeStamp int64) (token string, expireTime int64, err error) { h := sha256.New() h.Write( - []byte(config.Config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + config.Config.Push.GeTui.MasterSecret), + []byte(g.config.Push.GeTui.AppKey + strconv.Itoa(int(timeStamp)) + g.config.Push.GeTui.MasterSecret), ) sign := hex.EncodeToString(h.Sum(nil)) reqAuth := AuthReq{ Sign: sign, Timestamp: strconv.Itoa(int(timeStamp)), - AppKey: config.Config.Push.GeTui.AppKey, + AppKey: g.config.Push.GeTui.AppKey, } respAuth := AuthResp{} err = g.request(ctx, authURL, reqAuth, "", &respAuth) @@ -159,7 +164,7 @@ func (g *Client) request(ctx context.Context, url string, input any, token strin header := map[string]string{"token": token} resp := &Resp{} resp.Data = output - return g.postReturn(ctx, config.Config.Push.GeTui.PushUrl+url, header, input, resp, 3) + return g.postReturn(ctx, g.config.Push.GeTui.PushUrl+url, header, input, resp, 3) } func (g *Client) postReturn( diff --git a/internal/push/offlinepush/jpush/body/notification.go b/internal/push/offlinepush/jpush/body/notification.go index ddf3802af..b25882ea5 100644 --- a/internal/push/offlinepush/jpush/body/notification.go +++ b/internal/push/offlinepush/jpush/body/notification.go @@ -46,7 +46,6 @@ type Extras struct { func (n *Notification) SetAlert(alert string) { n.Alert = alert n.Android.Alert = alert - n.SetAndroidIntent() n.IOS.Alert = alert n.IOS.Sound = "default" n.IOS.Badge = "+1" @@ -57,8 +56,8 @@ func (n *Notification) SetExtras(extras Extras) { n.Android.Extras = extras } -func (n *Notification) SetAndroidIntent() { - n.Android.Intent.URL = config.Config.Push.Jpns.PushIntent +func (n *Notification) SetAndroidIntent(config *config.GlobalConfig) { + n.Android.Intent.URL = config.Push.Jpns.PushIntent } func (n *Notification) IOSEnableMutableContent() { diff --git a/internal/push/offlinepush/jpush/push.go b/internal/push/offlinepush/jpush/push.go index 567269f3c..2ced4bfd3 100644 --- a/internal/push/offlinepush/jpush/push.go +++ b/internal/push/offlinepush/jpush/push.go @@ -25,10 +25,12 @@ import ( http2 "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -type JPush struct{} +type JPush struct { + config *config.GlobalConfig +} -func NewClient() *JPush { - return &JPush{} +func NewClient(config *config.GlobalConfig) *JPush { + return &JPush{config: config} } func (j *JPush) Auth(apiKey, secretKey string, timeStamp int64) (token string, err error) { @@ -59,10 +61,12 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin no.IOSEnableMutableContent() no.SetExtras(extras) no.SetAlert(title) + no.SetAndroidIntent(j.config) + var msg body.Message msg.SetMsgContent(content) var opt body.Options - opt.SetApnsProduction(config.Config.IOSPush.Production) + opt.SetApnsProduction(j.config.IOSPush.Production) var pushObj body.PushObj pushObj.SetPlatform(&pf) pushObj.SetAudience(&au) @@ -76,9 +80,9 @@ func (j *JPush) Push(ctx context.Context, userIDs []string, title, content strin func (j *JPush) request(ctx context.Context, po body.PushObj, resp any, timeout int) error { return http2.PostReturn( ctx, - config.Config.Push.Jpns.PushUrl, + j.config.Push.Jpns.PushUrl, map[string]string{ - "Authorization": j.getAuthorization(config.Config.Push.Jpns.AppKey, config.Config.Push.Jpns.MasterSecret), + "Authorization": j.getAuthorization(j.config.Push.Jpns.AppKey, j.config.Push.Jpns.MasterSecret), }, po, resp, diff --git a/internal/push/push_handler.go b/internal/push/push_handler.go index 7b7a1150a..0e68e76b3 100644 --- a/internal/push/push_handler.go +++ b/internal/push/push_handler.go @@ -33,15 +33,29 @@ type ConsumerHandler struct { pusher *Pusher } -func NewConsumerHandler(pusher *Pusher) (*ConsumerHandler, error) { +func NewConsumerHandler(config *config.GlobalConfig, pusher *Pusher) (*ConsumerHandler, error) { var consumerHandler ConsumerHandler consumerHandler.pusher = pusher var err error + var tlsConfig *kfk.TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &kfk.TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: false, + } + } consumerHandler.pushConsumerGroup, err = kfk.NewMConsumerGroup(&kfk.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr, - config.Config.Kafka.ConsumerGroupID.MsgToPush) + OffsetsInitial: sarama.OffsetNewest, + IsReturnErr: false, + UserName: config.Kafka.Username, + Password: config.Kafka.Password, + }, []string{config.Kafka.MsgToPush.Topic}, config.Kafka.Addr, + config.Kafka.ConsumerGroupID.MsgToPush, + tlsConfig) if err != nil { return nil, err } diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index c5516e7cf..ed2a0b1ef 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,6 +16,7 @@ package push import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbpush "github.com/OpenIMSDK/protocol/push" @@ -31,20 +32,22 @@ import ( type pushServer struct { pusher *Pusher + config *config.GlobalConfig } -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - cacheModel := cache.NewMsgCacheModel(rdb) - offlinePusher := NewOfflinePusher(cacheModel) + cacheModel := cache.NewMsgCacheModel(rdb, config) + offlinePusher := NewOfflinePusher(config, cacheModel) database := controller.NewPushDatabase(cacheModel) - groupRpcClient := rpcclient.NewGroupRpcClient(client) - conversationRpcClient := rpcclient.NewConversationRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + conversationRpcClient := rpcclient.NewConversationRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) pusher := NewPusher( + config, client, offlinePusher, database, @@ -57,9 +60,10 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e pbpush.RegisterPushMsgServiceServer(server, &pushServer{ pusher: pusher, + config: config, }) - consumer, err := NewConsumer(pusher) + consumer, err := NewConsumer(config, pusher) if err != nil { return err } diff --git a/internal/push/push_to_client.go b/internal/push/push_to_client.go index 84efdba0c..49bce70ab 100644 --- a/internal/push/push_to_client.go +++ b/internal/push/push_to_client.go @@ -45,6 +45,7 @@ import ( ) type Pusher struct { + config *config.GlobalConfig database controller.PushDatabase discov discoveryregistry.SvcDiscoveryRegistry offlinePusher offlinepush.OfflinePusher @@ -57,11 +58,12 @@ type Pusher struct { var errNoOfflinePusher = errors.New("no offlinePusher is configured") -func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, +func NewPusher(config *config.GlobalConfig, discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offlinepush.OfflinePusher, database controller.PushDatabase, groupLocalCache *localcache.GroupLocalCache, conversationLocalCache *localcache.ConversationLocalCache, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient, msgRpcClient *rpcclient.MessageRpcClient, ) *Pusher { return &Pusher{ + config: config, discov: discov, database: database, offlinePusher: offlinePusher, @@ -73,15 +75,15 @@ func NewPusher(discov discoveryregistry.SvcDiscoveryRegistry, offlinePusher offl } } -func NewOfflinePusher(cache cache.MsgModel) offlinepush.OfflinePusher { +func NewOfflinePusher(config *config.GlobalConfig, cache cache.MsgModel) offlinepush.OfflinePusher { var offlinePusher offlinepush.OfflinePusher - switch config.Config.Push.Enable { + switch config.Push.Enable { case "getui": - offlinePusher = getui.NewClient(cache) + offlinePusher = getui.NewClient(config, cache) case "fcm": - offlinePusher = fcm.NewClient(cache) + offlinePusher = fcm.NewClient(config, cache) case "jpush": - offlinePusher = jpush.NewClient() + offlinePusher = jpush.NewClient(config) default: offlinePusher = dummy.NewClient() } @@ -99,7 +101,7 @@ func (p *Pusher) DeleteMemberAndSetConversationSeq(ctx context.Context, groupID func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.MsgData) error { log.ZDebug(ctx, "Get msg from msg_transfer And push msg", "userIDs", userIDs, "msg", msg.String()) - if err := callbackOnlinePush(ctx, userIDs, msg); err != nil { + if err := callbackOnlinePush(ctx, p.config, userIDs, msg); err != nil { return err } // push @@ -127,7 +129,7 @@ func (p *Pusher) Push2User(ctx context.Context, userIDs []string, msg *sdkws.Msg }) if len(offlinePushUserIDList) > 0 { - if err = callbackOfflinePush(ctx, offlinePushUserIDList, msg, &[]string{}); err != nil { + if err = callbackOfflinePush(ctx, p.config, offlinePushUserIDList, msg, &[]string{}); err != nil { return err } err = p.offlinePushMsg(ctx, msg.SendID, msg, offlinePushUserIDList) @@ -160,7 +162,7 @@ func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, } if len(needOfflinePushUserIDs) > 0 { var offlinePushUserIDs []string - err := callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + err := callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs) if err != nil { return err } @@ -191,7 +193,7 @@ func (p *Pusher) k8sOfflinePush2SuperGroup(ctx context.Context, groupID string, func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws.MsgData) (err error) { log.ZDebug(ctx, "Get super group msg from msg_transfer and push msg", "msg", msg.String(), "groupID", groupID) var pushToUserIDs []string - if err = callbackBeforeSuperGroupOnlinePush(ctx, groupID, msg, &pushToUserIDs); err != nil { + if err = callbackBeforeSuperGroupOnlinePush(ctx, p.config, groupID, msg, &pushToUserIDs); err != nil { return err } @@ -233,11 +235,11 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws return err } log.ZInfo(ctx, "GroupDismissedNotificationInfo****", "groupID", groupID, "num", len(pushToUserIDs), "list", pushToUserIDs) - if len(config.Config.Manager.UserID) > 0 { - ctx = mcontext.WithOpUserIDContext(ctx, config.Config.Manager.UserID[0]) + if len(p.config.Manager.UserID) > 0 { + ctx = mcontext.WithOpUserIDContext(ctx, p.config.Manager.UserID[0]) } - if len(config.Config.Manager.UserID) == 0 && len(config.Config.IMAdmin.UserID) > 0 { - ctx = mcontext.WithOpUserIDContext(ctx, config.Config.IMAdmin.UserID[0]) + if len(p.config.Manager.UserID) == 0 && len(p.config.IMAdmin.UserID) > 0 { + ctx = mcontext.WithOpUserIDContext(ctx, p.config.IMAdmin.UserID[0]) } defer func(groupID string) { if err = p.groupRpcClient.DismissGroup(ctx, groupID); err != nil { @@ -255,10 +257,10 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws log.ZDebug(ctx, "get conn and online push success", "result", wsResults, "msg", msg) isOfflinePush := utils.GetSwitchFromOptions(msg.Options, constant.IsOfflinePush) - if isOfflinePush && config.Config.Envs.Discovery == "k8s" { + if isOfflinePush && p.config.Envs.Discovery == "k8s" { return p.k8sOfflinePush2SuperGroup(ctx, groupID, msg, wsResults) } - if isOfflinePush && config.Config.Envs.Discovery == "zookeeper" { + if isOfflinePush && p.config.Envs.Discovery == "zookeeper" { var ( onlineSuccessUserIDs = []string{msg.SendID} webAndPcBackgroundUserIDs []string @@ -296,7 +298,7 @@ func (p *Pusher) Push2SuperGroup(ctx context.Context, groupID string, msg *sdkws // Use offline push messaging if len(needOfflinePushUserIDs) > 0 { var offlinePushUserIDs []string - err = callbackOfflinePush(ctx, needOfflinePushUserIDs, msg, &offlinePushUserIDs) + err = callbackOfflinePush(ctx, p.config, needOfflinePushUserIDs, msg, &offlinePushUserIDs) if err != nil { return err } @@ -355,7 +357,7 @@ func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUs var ( mu sync.Mutex wg = errgroup.Group{} - maxWorkers = config.Config.Push.MaxConcurrentWorkers + maxWorkers = p.config.Push.MaxConcurrentWorkers ) if maxWorkers < 3 { maxWorkers = 3 @@ -384,10 +386,10 @@ func (p *Pusher) k8sOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUs return wsResults, nil } func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, pushToUserIDs []string) (wsResults []*msggateway.SingleMsgToUserResults, err error) { - if config.Config.Envs.Discovery == "k8s" { + if p.config.Envs.Discovery == "k8s" { return p.k8sOnlinePush(ctx, msg, pushToUserIDs) } - conns, err := p.discov.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) + conns, err := p.discov.GetConns(ctx, p.config.RpcRegisterName.OpenImMessageGatewayName) log.ZDebug(ctx, "get gateway conn", "conn length", len(conns)) if err != nil { return nil, err @@ -397,7 +399,7 @@ func (p *Pusher) GetConnsAndOnlinePush(ctx context.Context, msg *sdkws.MsgData, mu sync.Mutex wg = errgroup.Group{} input = &msggateway.OnlineBatchPushOneMsgReq{MsgData: msg, PushToUserIDs: pushToUserIDs} - maxWorkers = config.Config.Push.MaxConcurrentWorkers + maxWorkers = p.config.Push.MaxConcurrentWorkers ) if maxWorkers < 3 { diff --git a/internal/rpc/auth/auth.go b/internal/rpc/auth/auth.go index 5f53911a4..eb1e2f68a 100644 --- a/internal/rpc/auth/auth.go +++ b/internal/rpc/auth/auth.go @@ -38,29 +38,32 @@ type authServer struct { authDatabase controller.AuthDatabase userRpcClient *rpcclient.UserRpcClient RegisterCenter discoveryregistry.SvcDiscoveryRegistry + config *config.GlobalConfig } -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - userRpcClient := rpcclient.NewUserRpcClient(client) + userRpcClient := rpcclient.NewUserRpcClient(client, config) pbauth.RegisterAuthServer(server, &authServer{ userRpcClient: &userRpcClient, RegisterCenter: client, authDatabase: controller.NewAuthDatabase( - cache.NewMsgCacheModel(rdb), - config.Config.Secret, - config.Config.TokenPolicy.Expire, + cache.NewMsgCacheModel(rdb, config), + config.Secret, + config.TokenPolicy.Expire, + config, ), + config: config, }) return nil } func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (*pbauth.UserTokenResp, error) { resp := pbauth.UserTokenResp{} - if req.Secret != config.Config.Secret { + if req.Secret != s.config.Secret { return nil, errs.ErrNoPermission.Wrap("secret invalid") } if _, err := s.userRpcClient.GetUserInfo(ctx, req.UserID); err != nil { @@ -72,17 +75,17 @@ func (s *authServer) UserToken(ctx context.Context, req *pbauth.UserTokenReq) (* } prommetrics.UserLoginCounter.Inc() resp.Token = token - resp.ExpireTimeSeconds = config.Config.TokenPolicy.Expire * 24 * 60 * 60 + resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60 return &resp, nil } func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenReq) (*pbauth.GetUserTokenResp, error) { - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, s.config); err != nil { return nil, err } resp := pbauth.GetUserTokenResp{} - if authverify.IsManagerUserID(req.UserID) { + if authverify.IsManagerUserID(req.UserID, s.config) { return nil, errs.ErrNoPermission.Wrap("don't get Admin token") } @@ -94,12 +97,12 @@ func (s *authServer) GetUserToken(ctx context.Context, req *pbauth.GetUserTokenR return nil, err } resp.Token = token - resp.ExpireTimeSeconds = config.Config.TokenPolicy.Expire * 24 * 60 * 60 + resp.ExpireTimeSeconds = s.config.TokenPolicy.Expire * 24 * 60 * 60 return &resp, nil } func (s *authServer) parseToken(ctx context.Context, tokensString string) (claims *tokenverify.Claims, err error) { - claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret()) + claims, err = tokenverify.GetClaimFromToken(tokensString, authverify.Secret(s.config.Secret)) if err != nil { return nil, errs.Wrap(err) } @@ -139,7 +142,7 @@ func (s *authServer) ParseToken( } func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq) (*pbauth.ForceLogoutResp, error) { - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, s.config); err != nil { return nil, err } if err := s.forceKickOff(ctx, req.UserID, req.PlatformID, mcontext.GetOperationID(ctx)); err != nil { @@ -149,7 +152,7 @@ func (s *authServer) ForceLogout(ctx context.Context, req *pbauth.ForceLogoutReq } func (s *authServer) forceKickOff(ctx context.Context, userID string, platformID int32, operationID string) error { - conns, err := s.RegisterCenter.GetConns(ctx, config.Config.RpcRegisterName.OpenImMessageGatewayName) + conns, err := s.RegisterCenter.GetConns(ctx, s.config.RpcRegisterName.OpenImMessageGatewayName) if err != nil { return err } diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index f3dc7ef17..cc08de298 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -17,6 +17,7 @@ package conversation import ( "context" "errors" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "sort" "github.com/OpenIMSDK/protocol/constant" @@ -44,6 +45,7 @@ type conversationServer struct { groupRpcClient *rpcclient.GroupRpcClient conversationDatabase controller.ConversationDatabase conversationNotificationSender *notification.ConversationNotificationSender + config *config.GlobalConfig } func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( @@ -54,28 +56,29 @@ func (c *conversationServer) GetConversationNotReceiveMessageUserIDs( panic("implement me") } -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return err } - conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase()) + conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) - userRpcClient := rpcclient.NewUserRpcClient(client) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) + userRpcClient := rpcclient.NewUserRpcClient(client, config) pbconversation.RegisterConversationServer(server, &conversationServer{ msgRpcClient: &msgRpcClient, user: &userRpcClient, - conversationNotificationSender: notification.NewConversationNotificationSender(&msgRpcClient), + conversationNotificationSender: notification.NewConversationNotificationSender(config, &msgRpcClient), groupRpcClient: &groupRpcClient, conversationDatabase: controller.NewConversationDatabase(conversationDB, cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), tx.NewMongo(mongo.GetClient())), + config: config, }) return nil } diff --git a/internal/rpc/friend/black.go b/internal/rpc/friend/black.go index 57edd26ba..64c63eb73 100644 --- a/internal/rpc/friend/black.go +++ b/internal/rpc/friend/black.go @@ -65,7 +65,7 @@ func (s *friendServer) RemoveBlack(ctx context.Context, req *pbfriend.RemoveBlac } func (s *friendServer) AddBlack(ctx context.Context, req *pbfriend.AddBlackReq) (*pbfriend.AddBlackResp, error) { - if err := authverify.CheckAccessV3(ctx, req.OwnerUserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config); err != nil { return nil, err } _, err := s.userRpcClient.GetUsersInfo(ctx, []string{req.OwnerUserID, req.BlackUserID}) diff --git a/internal/rpc/friend/callback.go b/internal/rpc/friend/callback.go index 2b6e31899..78d4fc926 100644 --- a/internal/rpc/friend/callback.go +++ b/internal/rpc/friend/callback.go @@ -24,8 +24,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -func CallbackBeforeAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) error { - if !config.Config.Callback.CallbackBeforeAddFriend.Enable { +func CallbackBeforeAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error { + if !globalConfig.Callback.CallbackBeforeAddFriend.Enable { return nil } cbReq := &cbapi.CallbackBeforeAddFriendReq{ @@ -36,14 +36,14 @@ func CallbackBeforeAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriend Ex: req.Ex, } resp := &cbapi.CallbackBeforeAddFriendResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeAddFriend); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil { return err } return nil } -func CallbackBeforeSetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) error { - if !config.Config.Callback.CallbackBeforeSetFriendRemark.Enable { +func CallbackBeforeSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error { + if !globalConfig.Callback.CallbackBeforeSetFriendRemark.Enable { return nil } cbReq := &cbapi.CallbackBeforeSetFriendRemarkReq{ @@ -53,15 +53,15 @@ func CallbackBeforeSetFriendRemark(ctx context.Context, req *pbfriend.SetFriendR Remark: req.Remark, } resp := &cbapi.CallbackBeforeSetFriendRemarkResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeAddFriend); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil { return err } utils.NotNilReplace(&req.Remark, &resp.Remark) return nil } -func CallbackAfterSetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) error { - if !config.Config.Callback.CallbackAfterSetFriendRemark.Enable { +func CallbackAfterSetFriendRemark(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.SetFriendRemarkReq) error { + if !globalConfig.Callback.CallbackAfterSetFriendRemark.Enable { return nil } cbReq := &cbapi.CallbackAfterSetFriendRemarkReq{ @@ -71,13 +71,13 @@ func CallbackAfterSetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRe Remark: req.Remark, } resp := &cbapi.CallbackAfterSetFriendRemarkResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeAddFriend); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriend); err != nil { return err } return nil } -func CallbackBeforeAddBlack(ctx context.Context, req *pbfriend.AddBlackReq) error { - if !config.Config.Callback.CallbackBeforeAddBlack.Enable { +func CallbackBeforeAddBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.AddBlackReq) error { + if !globalConfig.Callback.CallbackBeforeAddBlack.Enable { return nil } cbReq := &cbapi.CallbackBeforeAddBlackReq{ @@ -86,13 +86,13 @@ func CallbackBeforeAddBlack(ctx context.Context, req *pbfriend.AddBlackReq) erro BlackUserID: req.BlackUserID, } resp := &cbapi.CallbackBeforeAddBlackResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeAddBlack); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddBlack); err != nil { return err } return nil } -func CallbackAfterAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) error { - if !config.Config.Callback.CallbackAfterAddFriend.Enable { +func CallbackAfterAddFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ApplyToAddFriendReq) error { + if !globalConfig.Callback.CallbackAfterAddFriend.Enable { return nil } cbReq := &cbapi.CallbackAfterAddFriendReq{ @@ -102,14 +102,14 @@ func CallbackAfterAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendR ReqMsg: req.ReqMsg, } resp := &cbapi.CallbackAfterAddFriendResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterAddFriend); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterAddFriend); err != nil { return err } return nil } -func CallbackBeforeAddFriendAgree(ctx context.Context, req *pbfriend.RespondFriendApplyReq) error { - if !config.Config.Callback.CallbackBeforeAddFriendAgree.Enable { +func CallbackBeforeAddFriendAgree(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RespondFriendApplyReq) error { + if !globalConfig.Callback.CallbackBeforeAddFriendAgree.Enable { return nil } cbReq := &cbapi.CallbackBeforeAddFriendAgreeReq{ @@ -120,13 +120,13 @@ func CallbackBeforeAddFriendAgree(ctx context.Context, req *pbfriend.RespondFrie HandleResult: req.HandleResult, } resp := &cbapi.CallbackBeforeAddFriendAgreeResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeAddFriendAgree); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeAddFriendAgree); err != nil { return err } return nil } -func CallbackAfterDeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendReq) error { - if !config.Config.Callback.CallbackAfterDeleteFriend.Enable { +func CallbackAfterDeleteFriend(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.DeleteFriendReq) error { + if !globalConfig.Callback.CallbackAfterDeleteFriend.Enable { return nil } cbReq := &cbapi.CallbackAfterDeleteFriendReq{ @@ -135,13 +135,13 @@ func CallbackAfterDeleteFriend(ctx context.Context, req *pbfriend.DeleteFriendRe FriendUserID: req.FriendUserID, } resp := &cbapi.CallbackAfterDeleteFriendResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterDeleteFriend); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterDeleteFriend); err != nil { return err } return nil } -func CallbackBeforeImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) error { - if !config.Config.Callback.CallbackBeforeImportFriends.Enable { +func CallbackBeforeImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error { + if !globalConfig.Callback.CallbackBeforeImportFriends.Enable { return nil } cbReq := &cbapi.CallbackBeforeImportFriendsReq{ @@ -150,7 +150,7 @@ func CallbackBeforeImportFriends(ctx context.Context, req *pbfriend.ImportFriend FriendUserIDs: req.FriendUserIDs, } resp := &cbapi.CallbackBeforeImportFriendsResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeImportFriends); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeImportFriends); err != nil { return err } if len(resp.FriendUserIDs) != 0 { @@ -158,8 +158,8 @@ func CallbackBeforeImportFriends(ctx context.Context, req *pbfriend.ImportFriend } return nil } -func CallbackAfterImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) error { - if !config.Config.Callback.CallbackAfterImportFriends.Enable { +func CallbackAfterImportFriends(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.ImportFriendReq) error { + if !globalConfig.Callback.CallbackAfterImportFriends.Enable { return nil } cbReq := &cbapi.CallbackAfterImportFriendsReq{ @@ -168,14 +168,14 @@ func CallbackAfterImportFriends(ctx context.Context, req *pbfriend.ImportFriendR FriendUserIDs: req.FriendUserIDs, } resp := &cbapi.CallbackAfterImportFriendsResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterImportFriends); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterImportFriends); err != nil { return err } return nil } -func CallbackAfterRemoveBlack(ctx context.Context, req *pbfriend.RemoveBlackReq) error { - if !config.Config.Callback.CallbackAfterRemoveBlack.Enable { +func CallbackAfterRemoveBlack(ctx context.Context, globalConfig *config.GlobalConfig, req *pbfriend.RemoveBlackReq) error { + if !globalConfig.Callback.CallbackAfterRemoveBlack.Enable { return nil } cbReq := &cbapi.CallbackAfterRemoveBlackReq{ @@ -184,7 +184,7 @@ func CallbackAfterRemoveBlack(ctx context.Context, req *pbfriend.RemoveBlackReq) BlackUserID: req.BlackUserID, } resp := &cbapi.CallbackAfterRemoveBlackResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterRemoveBlack); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterRemoveBlack); err != nil { return err } return nil diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index 4168731f7..ffdeee98f 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -16,25 +16,33 @@ package friend import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + + "github.com/OpenIMSDK/tools/tx" + + "github.com/OpenIMSDK/protocol/sdkws" + + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + + "github.com/OpenIMSDK/tools/log" + + "github.com/openimsdk/open-im-server/v3/pkg/common/convert" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + + "google.golang.org/grpc" "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" - "github.com/OpenIMSDK/protocol/sdkws" registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" - "google.golang.org/grpc" ) type friendServer struct { @@ -44,42 +52,44 @@ type friendServer struct { notificationSender *notification.FriendNotificationSender conversationRpcClient rpcclient.ConversationRpcClient RegisterCenter registry.SvcDiscoveryRegistry + config *config.GlobalConfig } -func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { +func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error { // Initialize MongoDB - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return err } // Initialize Redis - rdb, err := cache.NewRedis() + rdb, err := cache.NewRedis(config) if err != nil { return err } - friendMongoDB, err := mgo.NewFriendMongo(mongo.GetDatabase()) + friendMongoDB, err := mgo.NewFriendMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mongo.GetDatabase()) + friendRequestMongoDB, err := mgo.NewFriendRequestMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - blackMongoDB, err := mgo.NewBlackMongo(mongo.GetDatabase()) + blackMongoDB, err := mgo.NewBlackMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } // Initialize RPC clients - userRpcClient := rpcclient.NewUserRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) + userRpcClient := rpcclient.NewUserRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) // Initialize notification sender notificationSender := notification.NewFriendNotificationSender( + config, &msgRpcClient, notification.WithRpcFunc(userRpcClient.GetUsersInfo), ) @@ -98,7 +108,8 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { userRpcClient: &userRpcClient, notificationSender: notificationSender, RegisterCenter: client, - conversationRpcClient: rpcclient.NewConversationRpcClient(client), + conversationRpcClient: rpcclient.NewConversationRpcClient(client, config), + config: config, }) return nil @@ -106,15 +117,14 @@ func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { // ok. func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) { - defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.ApplyToAddFriendResp{} - if err := authverify.CheckAccessV3(ctx, req.FromUserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.FromUserID,s.config); err != nil { return nil, err } if req.ToUserID == req.FromUserID { - return nil, errs.ErrCanNotAddYourself.Wrap() + return nil, errs.ErrCanNotAddYourself.Wrap("req.ToUserID", req.ToUserID) } - if err = CallbackBeforeAddFriend(ctx, req); err != nil && err != errs.ErrCallbackContinue { + if err = CallbackBeforeAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } if _, err := s.userRpcClient.GetUsersInfoMap(ctx, []string{req.ToUserID, req.FromUserID}); err != nil { @@ -131,7 +141,7 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply return nil, err } s.notificationSender.FriendApplicationAddNotification(ctx, req) - if err = CallbackAfterAddFriend(ctx, req); err != nil && err != errs.ErrCallbackContinue { + if err = CallbackAfterAddFriend(ctx,s.config ,req); err != nil && err != errs.ErrCallbackContinue { return nil, err } return resp, nil @@ -140,7 +150,7 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply // ok. func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFriendReq) (resp *pbfriend.ImportFriendResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, s.config); err != nil { return nil, err } if _, err := s.userRpcClient.GetUsersInfo(ctx, append([]string{req.OwnerUserID}, req.FriendUserIDs...)); err != nil { @@ -152,7 +162,7 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr if utils.Duplicate(req.FriendUserIDs) { return nil, errs.ErrArgs.Wrap("friend userID repeated") } - if err := CallbackBeforeImportFriends(ctx, req); err != nil { + if err := CallbackBeforeImportFriends(ctx, s.config, req); err != nil { return nil, err } @@ -166,7 +176,7 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr HandleResult: constant.FriendResponseAgree, }) } - if err := CallbackAfterImportFriends(ctx, req); err != nil { + if err := CallbackAfterImportFriends(ctx, s.config, req); err != nil { return nil, err } return &pbfriend.ImportFriendResp{}, nil @@ -176,7 +186,7 @@ func (s *friendServer) ImportFriends(ctx context.Context, req *pbfriend.ImportFr func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.RespondFriendApplyReq) (resp *pbfriend.RespondFriendApplyResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") resp = &pbfriend.RespondFriendApplyResp{} - if err := authverify.CheckAccessV3(ctx, req.ToUserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.ToUserID, s.config); err != nil { return nil, err } @@ -187,7 +197,7 @@ func (s *friendServer) RespondFriendApply(ctx context.Context, req *pbfriend.Res HandleResult: req.HandleResult, } if req.HandleResult == constant.FriendResponseAgree { - if err := CallbackBeforeAddFriendAgree(ctx, req); err != nil && err != errs.ErrCallbackContinue { + if err := CallbackBeforeAddFriendAgree(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } err := s.friendDatabase.AgreeFriendRequest(ctx, &friendRequest) @@ -223,7 +233,7 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri return nil, err } s.notificationSender.FriendDeletedNotification(ctx, req) - if err := CallbackAfterDeleteFriend(ctx, req); err != nil { + if err := CallbackAfterDeleteFriend(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -233,7 +243,7 @@ func (s *friendServer) DeleteFriend(ctx context.Context, req *pbfriend.DeleteFri func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFriendRemarkReq) (resp *pbfriend.SetFriendRemarkResp, err error) { defer log.ZInfo(ctx, utils.GetFuncName()+" Return") - if err = CallbackBeforeSetFriendRemark(ctx, req); err != nil && err != errs.ErrCallbackContinue { + if err = CallbackBeforeSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } resp = &pbfriend.SetFriendRemarkResp{} @@ -247,7 +257,7 @@ func (s *friendServer) SetFriendRemark(ctx context.Context, req *pbfriend.SetFri if err := s.friendDatabase.UpdateRemark(ctx, req.OwnerUserID, req.FriendUserID, req.Remark); err != nil { return nil, err } - if err := CallbackAfterSetFriendRemark(ctx, req); err != nil && err != errs.ErrCallbackContinue { + if err := CallbackAfterSetFriendRemark(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } s.notificationSender.FriendRemarkSetNotification(ctx, req.OwnerUserID, req.FriendUserID) diff --git a/internal/rpc/group/callback.go b/internal/rpc/group/callback.go index f09c23ec6..e82177dde 100644 --- a/internal/rpc/group/callback.go +++ b/internal/rpc/group/callback.go @@ -32,8 +32,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -func CallbackBeforeCreateGroup(ctx context.Context, req *group.CreateGroupReq) (err error) { - if !config.Config.Callback.CallbackBeforeCreateGroup.Enable { +func CallbackBeforeCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) { + if !globalConfig.Callback.CallbackBeforeCreateGroup.Enable { return nil } cbReq := &callbackstruct.CallbackBeforeCreateGroupReq{ @@ -58,7 +58,7 @@ func CallbackBeforeCreateGroup(ctx context.Context, req *group.CreateGroupReq) ( }) } resp := &callbackstruct.CallbackBeforeCreateGroupResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeCreateGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeCreateGroup); err != nil { return err } utils.NotNilReplace(&req.GroupInfo.GroupID, resp.GroupID) @@ -76,8 +76,8 @@ func CallbackBeforeCreateGroup(ctx context.Context, req *group.CreateGroupReq) ( return nil } -func CallbackAfterCreateGroup(ctx context.Context, req *group.CreateGroupReq) (err error) { - if !config.Config.Callback.CallbackAfterCreateGroup.Enable { +func CallbackAfterCreateGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.CreateGroupReq) (err error) { + if !globalConfig.Callback.CallbackAfterCreateGroup.Enable { return nil } cbReq := &callbackstruct.CallbackAfterCreateGroupReq{ @@ -101,7 +101,7 @@ func CallbackAfterCreateGroup(ctx context.Context, req *group.CreateGroupReq) (e }) } resp := &callbackstruct.CallbackAfterCreateGroupResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterCreateGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterCreateGroup); err != nil { return err } return nil @@ -109,10 +109,11 @@ func CallbackAfterCreateGroup(ctx context.Context, req *group.CreateGroupReq) (e func CallbackBeforeMemberJoinGroup( ctx context.Context, + globalConfig *config.GlobalConfig, groupMember *relation.GroupMemberModel, groupEx string, ) (err error) { - if !config.Config.Callback.CallbackBeforeMemberJoinGroup.Enable { + if !globalConfig.Callback.CallbackBeforeMemberJoinGroup.Enable { return nil } callbackReq := &callbackstruct.CallbackBeforeMemberJoinGroupReq{ @@ -125,10 +126,10 @@ func CallbackBeforeMemberJoinGroup( resp := &callbackstruct.CallbackBeforeMemberJoinGroupResp{} err = http.CallBackPostReturn( ctx, - config.Config.Callback.CallbackUrl, + globalConfig.Callback.CallbackUrl, callbackReq, resp, - config.Config.Callback.CallbackBeforeMemberJoinGroup, + globalConfig.Callback.CallbackBeforeMemberJoinGroup, ) if err != nil { return err @@ -143,8 +144,8 @@ func CallbackBeforeMemberJoinGroup( return nil } -func CallbackBeforeSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMemberInfo) (err error) { - if !config.Config.Callback.CallbackBeforeSetGroupMemberInfo.Enable { +func CallbackBeforeSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) { + if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable { return nil } callbackReq := callbackstruct.CallbackBeforeSetGroupMemberInfoReq{ @@ -167,10 +168,10 @@ func CallbackBeforeSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMe resp := &callbackstruct.CallbackBeforeSetGroupMemberInfoResp{} err = http.CallBackPostReturn( ctx, - config.Config.Callback.CallbackUrl, + globalConfig.Callback.CallbackUrl, callbackReq, resp, - config.Config.Callback.CallbackBeforeSetGroupMemberInfo, + globalConfig.Callback.CallbackBeforeSetGroupMemberInfo, ) if err != nil { return err @@ -189,8 +190,8 @@ func CallbackBeforeSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMe } return nil } -func CallbackAfterSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMemberInfo) (err error) { - if !config.Config.Callback.CallbackBeforeSetGroupMemberInfo.Enable { +func CallbackAfterSetGroupMemberInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupMemberInfo) (err error) { + if !globalConfig.Callback.CallbackBeforeSetGroupMemberInfo.Enable { return nil } callbackReq := callbackstruct.CallbackAfterSetGroupMemberInfoReq{ @@ -211,14 +212,14 @@ func CallbackAfterSetGroupMemberInfo(ctx context.Context, req *group.SetGroupMem callbackReq.Ex = &req.Ex.Value } resp := &callbackstruct.CallbackAfterSetGroupMemberInfoResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterSetGroupMemberInfo); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupMemberInfo); err != nil { return err } return nil } -func CallbackQuitGroup(ctx context.Context, req *group.QuitGroupReq) (err error) { - if !config.Config.Callback.CallbackQuitGroup.Enable { +func CallbackQuitGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.QuitGroupReq) (err error) { + if !globalConfig.Callback.CallbackQuitGroup.Enable { return nil } cbReq := &callbackstruct.CallbackQuitGroupReq{ @@ -227,14 +228,14 @@ func CallbackQuitGroup(ctx context.Context, req *group.QuitGroupReq) (err error) UserID: req.UserID, } resp := &callbackstruct.CallbackQuitGroupResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackQuitGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil { return err } return nil } -func CallbackKillGroupMember(ctx context.Context, req *pbgroup.KickGroupMemberReq) (err error) { - if !config.Config.Callback.CallbackKillGroupMember.Enable { +func CallbackKillGroupMember(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.KickGroupMemberReq) (err error) { + if !globalConfig.Callback.CallbackKillGroupMember.Enable { return nil } cbReq := &callbackstruct.CallbackKillGroupMemberReq{ @@ -243,41 +244,41 @@ func CallbackKillGroupMember(ctx context.Context, req *pbgroup.KickGroupMemberRe KickedUserIDs: req.KickedUserIDs, } resp := &callbackstruct.CallbackKillGroupMemberResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackQuitGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackQuitGroup); err != nil { return err } return nil } -func CallbackDismissGroup(ctx context.Context, req *callbackstruct.CallbackDisMissGroupReq) (err error) { - if !config.Config.Callback.CallbackDismissGroup.Enable { +func CallbackDismissGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackDisMissGroupReq) (err error) { + if !globalConfig.Callback.CallbackDismissGroup.Enable { return nil } req.CallbackCommand = callbackstruct.CallbackDisMissGroupCommand resp := &callbackstruct.CallbackDisMissGroupResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, req, resp, config.Config.Callback.CallbackQuitGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackQuitGroup); err != nil { return err } return nil } -func CallbackApplyJoinGroupBefore(ctx context.Context, req *callbackstruct.CallbackJoinGroupReq) (err error) { - if !config.Config.Callback.CallbackBeforeJoinGroup.Enable { +func CallbackApplyJoinGroupBefore(ctx context.Context, globalConfig *config.GlobalConfig, req *callbackstruct.CallbackJoinGroupReq) (err error) { + if !globalConfig.Callback.CallbackBeforeJoinGroup.Enable { return nil } req.CallbackCommand = callbackstruct.CallbackBeforeJoinGroupCommand resp := &callbackstruct.CallbackJoinGroupResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, req, resp, config.Config.Callback.CallbackBeforeJoinGroup); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeJoinGroup); err != nil { return err } return nil } -func CallbackAfterTransferGroupOwner(ctx context.Context, req *pbgroup.TransferGroupOwnerReq) (err error) { - if !config.Config.Callback.CallbackAfterTransferGroupOwner.Enable { +func CallbackAfterTransferGroupOwner(ctx context.Context, globalConfig *config.GlobalConfig, req *pbgroup.TransferGroupOwnerReq) (err error) { + if !globalConfig.Callback.CallbackAfterTransferGroupOwner.Enable { return nil } @@ -289,13 +290,13 @@ func CallbackAfterTransferGroupOwner(ctx context.Context, req *pbgroup.TransferG } resp := &callbackstruct.CallbackTransferGroupOwnerResp{} - if err = http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterTransferGroupOwner); err != nil { + if err = http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterTransferGroupOwner); err != nil { return err } return nil } -func CallbackBeforeInviteUserToGroup(ctx context.Context, req *group.InviteUserToGroupReq) (err error) { - if !config.Config.Callback.CallbackBeforeInviteUserToGroup.Enable { +func CallbackBeforeInviteUserToGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.InviteUserToGroupReq) (err error) { + if !globalConfig.Callback.CallbackBeforeInviteUserToGroup.Enable { return nil } @@ -310,10 +311,10 @@ func CallbackBeforeInviteUserToGroup(ctx context.Context, req *group.InviteUserT resp := &callbackstruct.CallbackBeforeInviteUserToGroupResp{} err = http.CallBackPostReturn( ctx, - config.Config.Callback.CallbackUrl, + globalConfig.Callback.CallbackUrl, callbackReq, resp, - config.Config.Callback.CallbackBeforeInviteUserToGroup, + globalConfig.Callback.CallbackBeforeInviteUserToGroup, ) if err != nil { @@ -327,8 +328,8 @@ func CallbackBeforeInviteUserToGroup(ctx context.Context, req *group.InviteUserT return nil } -func CallbackAfterJoinGroup(ctx context.Context, req *group.JoinGroupReq) error { - if !config.Config.Callback.CallbackAfterJoinGroup.Enable { +func CallbackAfterJoinGroup(ctx context.Context, globalConfig *config.GlobalConfig, req *group.JoinGroupReq) error { + if !globalConfig.Callback.CallbackAfterJoinGroup.Enable { return nil } callbackReq := &callbackstruct.CallbackAfterJoinGroupReq{ @@ -340,14 +341,14 @@ func CallbackAfterJoinGroup(ctx context.Context, req *group.JoinGroupReq) error InviterUserID: req.InviterUserID, } resp := &callbackstruct.CallbackAfterJoinGroupResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterJoinGroup); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterJoinGroup); err != nil { return err } return nil } -func CallbackBeforeSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) error { - if !config.Config.Callback.CallbackBeforeSetGroupInfo.Enable { +func CallbackBeforeSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error { + if !globalConfig.Callback.CallbackBeforeSetGroupInfo.Enable { return nil } callbackReq := &callbackstruct.CallbackBeforeSetGroupInfoReq{ @@ -374,7 +375,7 @@ func CallbackBeforeSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) } resp := &callbackstruct.CallbackBeforeSetGroupInfoResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackBeforeSetGroupInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackBeforeSetGroupInfo); err != nil { return err } @@ -396,8 +397,8 @@ func CallbackBeforeSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) utils.NotNilReplace(&req.GroupInfoForSet.Introduction, &resp.Introduction) return nil } -func CallbackAfterSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) error { - if !config.Config.Callback.CallbackAfterSetGroupInfo.Enable { +func CallbackAfterSetGroupInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *group.SetGroupInfoReq) error { + if !globalConfig.Callback.CallbackAfterSetGroupInfo.Enable { return nil } callbackReq := &callbackstruct.CallbackAfterSetGroupInfoReq{ @@ -421,7 +422,7 @@ func CallbackAfterSetGroupInfo(ctx context.Context, req *group.SetGroupInfoReq) callbackReq.ApplyMemberFriend = &req.GroupInfoForSet.ApplyMemberFriend.Value } resp := &callbackstruct.CallbackAfterSetGroupInfoResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterSetGroupInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterSetGroupInfo); err != nil { return err } return nil diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index fa060efda..325c1edce 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -17,6 +17,7 @@ package group import ( "context" "fmt" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "math/big" "math/rand" "strconv" @@ -50,35 +51,35 @@ import ( "google.golang.org/grpc" ) -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - mongo, err := unrelation.NewMongo() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + mongo, err := unrelation.NewMongo(config) if err != nil { return err } - rdb, err := cache.NewRedis() + rdb, err := cache.NewRedis(config) if err != nil { return err } - groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase()) + groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase()) + groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase()) + groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - userRpcClient := rpcclient.NewUserRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) - conversationRpcClient := rpcclient.NewConversationRpcClient(client) + userRpcClient := rpcclient.NewUserRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) + conversationRpcClient := rpcclient.NewConversationRpcClient(client, config) var gs groupServer database := controller.NewGroupDatabase(rdb, groupDB, groupMemberDB, groupRequestDB, tx.NewMongo(mongo.GetClient()), grouphash.NewGroupHashFromGroupServer(&gs)) gs.db = database gs.User = userRpcClient - gs.Notification = notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { + gs.Notification = notification.NewGroupNotificationSender(database, &msgRpcClient, &userRpcClient, config, func(ctx context.Context, userIDs []string) ([]notification.CommonUser, error) { users, err := userRpcClient.GetUsersInfo(ctx, userIDs) if err != nil { return nil, err @@ -87,6 +88,7 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e }) gs.conversationRpcClient = conversationRpcClient gs.msgRpcClient = msgRpcClient + gs.config = config pbgroup.RegisterGroupServer(server, &gs) return nil } @@ -97,6 +99,7 @@ type groupServer struct { Notification *notification.GroupNotificationSender conversationRpcClient rpcclient.ConversationRpcClient msgRpcClient rpcclient.MessageRpcClient + config *config.GlobalConfig } func (s *groupServer) GetJoinedGroupIDs(ctx context.Context, req *pbgroup.GetJoinedGroupIDsReq) (*pbgroup.GetJoinedGroupIDsResp, error) { @@ -105,7 +108,6 @@ func (s *groupServer) GetJoinedGroupIDs(ctx context.Context, req *pbgroup.GetJoi } func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgroup.NotificationUserInfoUpdateReq) (*pbgroup.NotificationUserInfoUpdateResp, error) { - defer log.ZDebug(ctx, "NotificationUserInfoUpdate return") members, err := s.db.FindGroupMemberUser(ctx, nil, req.UserID) if err != nil { return nil, err @@ -117,7 +119,6 @@ func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgro } groupIDs = append(groupIDs, member.GroupID) } - log.ZInfo(ctx, "NotificationUserInfoUpdate", "joinGroupNum", len(members), "updateNum", len(groupIDs), "updateGroupIDs", groupIDs) for _, groupID := range groupIDs { if err := s.Notification.GroupMemberInfoSetNotification(ctx, groupID, req.UserID); err != nil { log.ZError(ctx, "NotificationUserInfoUpdate setGroupMemberInfo notification failed", err, "groupID", groupID) @@ -131,7 +132,7 @@ func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgro } func (s *groupServer) CheckGroupAdmin(ctx context.Context, groupID string) error { - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { groupMember, err := s.db.TakeGroupMember(ctx, groupID, mcontext.GetOpUserID(ctx)) if err != nil { return err @@ -196,7 +197,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR if req.OwnerUserID == "" { return nil, errs.ErrArgs.Wrap("no group owner") } - if err := authverify.CheckAccessV3(ctx, req.OwnerUserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.OwnerUserID, s.config); err != nil { return nil, err } userIDs := append(append(req.MemberUserIDs, req.AdminUserIDs...), req.OwnerUserID) @@ -215,7 +216,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR return nil, errs.ErrUserIDNotFound.Wrap("user not found") } // Callback Before create Group - if err := CallbackBeforeCreateGroup(ctx, req); err != nil { + if err := CallbackBeforeCreateGroup(ctx, s.config, req); err != nil { return nil, err } var groupMembers []*relationtb.GroupMemberModel @@ -234,7 +235,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR JoinTime: time.Now(), MuteEndTime: time.UnixMilli(0), } - if err := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); err != nil { + if err := CallbackBeforeMemberJoinGroup(ctx, s.config, groupMember, group.Ex); err != nil { return err } groupMembers = append(groupMembers, groupMember) @@ -302,7 +303,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR AdminUserIDs: req.AdminUserIDs, } - if err := CallbackAfterCreateGroup(ctx, reqCallBackAfter); err != nil { + if err := CallbackAfterCreateGroup(ctx, s.config, reqCallBackAfter); err != nil { return nil, err } @@ -311,7 +312,7 @@ func (s *groupServer) CreateGroup(ctx context.Context, req *pbgroup.CreateGroupR func (s *groupServer) GetJoinedGroupList(ctx context.Context, req *pbgroup.GetJoinedGroupListReq) (*pbgroup.GetJoinedGroupListResp, error) { resp := &pbgroup.GetJoinedGroupListResp{} - if err := authverify.CheckAccessV3(ctx, req.FromUserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil { return nil, err } total, members, err := s.db.PageGetJoinGroup(ctx, req.FromUserID, req.Pagination) @@ -381,7 +382,7 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite } var groupMember *relationtb.GroupMemberModel var opUserID string - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { opUserID = mcontext.GetOpUserID(ctx) var err error groupMember, err = s.db.TakeGroupMember(ctx, req.GroupID, opUserID) @@ -393,11 +394,11 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite } } - if err := CallbackBeforeInviteUserToGroup(ctx, req); err != nil { + if err := CallbackBeforeInviteUserToGroup(ctx, s.config, req); err != nil { return nil, err } if group.NeedVerification == constant.AllNeedVerification { - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { if !(groupMember.RoleLevel == constant.GroupOwner || groupMember.RoleLevel == constant.GroupAdmin) { var requests []*relationtb.GroupRequestModel for _, userID := range req.InvitedUserIDs { @@ -437,7 +438,7 @@ func (s *groupServer) InviteUserToGroup(ctx context.Context, req *pbgroup.Invite JoinTime: time.Now(), MuteEndTime: time.UnixMilli(0), } - if err := CallbackBeforeMemberJoinGroup(ctx, member, group.Ex); err != nil { + if err := CallbackBeforeMemberJoinGroup(ctx, s.config, member, group.Ex); err != nil { return nil, err } groupMembers = append(groupMembers, member) @@ -537,7 +538,7 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou for i, member := range members { memberMap[member.UserID] = members[i] } - isAppManagerUid := authverify.IsAppManagerUid(ctx) + isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config) opMember := memberMap[opUserID] for _, userID := range req.KickedUserIDs { member, ok := memberMap[userID] @@ -609,7 +610,7 @@ func (s *groupServer) KickGroupMember(ctx context.Context, req *pbgroup.KickGrou return nil, err } - if err := CallbackKillGroupMember(ctx, req); err != nil { + if err := CallbackKillGroupMember(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -735,7 +736,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup if !utils.Contain(req.HandleResult, constant.GroupResponseAgree, constant.GroupResponseRefuse) { return nil, errs.ErrArgs.Wrap("HandleResult unknown") } - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { groupMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx)) if err != nil { return nil, err @@ -779,7 +780,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup OperatorUserID: mcontext.GetOpUserID(ctx), Ex: groupRequest.Ex, } - if err = CallbackBeforeMemberJoinGroup(ctx, member, group.Ex); err != nil { + if err = CallbackBeforeMemberJoinGroup(ctx, s.config, member, group.Ex); err != nil { return nil, err } } @@ -827,7 +828,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) Ex: req.Ex, } - if err = CallbackApplyJoinGroupBefore(ctx, reqCall); err != nil { + if err = CallbackApplyJoinGroupBefore(ctx, s.config, reqCall); err != nil { return nil, err } _, err = s.db.TakeGroupMember(ctx, req.GroupID, req.InviterUserID) @@ -848,7 +849,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) JoinTime: time.Now(), MuteEndTime: time.UnixMilli(0), } - if err := CallbackBeforeMemberJoinGroup(ctx, groupMember, group.Ex); err != nil { + if err := CallbackBeforeMemberJoinGroup(ctx, s.config, groupMember, group.Ex); err != nil { return nil, err } if err := s.db.CreateGroup(ctx, nil, []*relationtb.GroupMemberModel{groupMember}); err != nil { @@ -859,7 +860,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) return nil, err } s.Notification.MemberEnterNotification(ctx, req.GroupID, req.InviterUserID) - if err = CallbackAfterJoinGroup(ctx, req); err != nil { + if err = CallbackAfterJoinGroup(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -873,7 +874,7 @@ func (s *groupServer) JoinGroup(ctx context.Context, req *pbgroup.JoinGroupReq) HandledTime: time.Unix(0, 0), Ex: req.Ex, } - if err := s.db.CreateGroupRequest(ctx, []*relationtb.GroupRequestModel{&groupRequest}); err != nil { + if err = s.db.CreateGroupRequest(ctx, []*relationtb.GroupRequestModel{&groupRequest}); err != nil { return nil, err } s.Notification.JoinGroupApplicationNotification(ctx, req) @@ -885,7 +886,7 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq) if req.UserID == "" { req.UserID = mcontext.GetOpUserID(ctx) } else { - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, s.config); err != nil { return nil, err } } @@ -909,7 +910,7 @@ func (s *groupServer) QuitGroup(ctx context.Context, req *pbgroup.QuitGroupReq) } // callback - if err := CallbackQuitGroup(ctx, req); err != nil { + if err := CallbackQuitGroup(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -926,7 +927,7 @@ func (s *groupServer) deleteMemberAndSetConversationSeq(ctx context.Context, gro func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInfoReq) (*pbgroup.SetGroupInfoResp, error) { var opMember *relationtb.GroupMemberModel - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { var err error opMember, err = s.db.TakeGroupMember(ctx, req.GroupInfoForSet.GroupID, mcontext.GetOpUserID(ctx)) if err != nil { @@ -939,7 +940,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf return nil, err } } - if err := CallbackBeforeSetGroupInfo(ctx, req); err != nil { + if err := CallbackBeforeSetGroupInfo(ctx, s.config, req); err != nil { return nil, err } group, err := s.db.TakeGroup(ctx, req.GroupInfoForSet.GroupID) @@ -1008,7 +1009,7 @@ func (s *groupServer) SetGroupInfo(ctx context.Context, req *pbgroup.SetGroupInf if num > 0 { _ = s.Notification.GroupInfoSetNotification(ctx, tips) } - if err := CallbackAfterSetGroupInfo(ctx, req); err != nil { + if err := CallbackAfterSetGroupInfo(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -1045,7 +1046,7 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans if newOwner == nil { return nil, errs.ErrArgs.Wrap("NewOwnerUser not in group " + req.NewOwnerUserID) } - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { if !(mcontext.GetOpUserID(ctx) == oldOwner.UserID && oldOwner.RoleLevel == constant.GroupOwner) { return nil, errs.ErrNoPermission.Wrap("no permission transfer group owner") } @@ -1054,7 +1055,7 @@ func (s *groupServer) TransferGroupOwner(ctx context.Context, req *pbgroup.Trans return nil, err } - if err := CallbackAfterTransferGroupOwner(ctx, req); err != nil { + if err := CallbackAfterTransferGroupOwner(ctx, s.config, req); err != nil { return nil, err } s.Notification.GroupOwnerTransferredNotification(ctx, req) @@ -1186,7 +1187,7 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou if err != nil { return nil, err } - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { if owner.UserID != mcontext.GetOpUserID(ctx) { return nil, errs.ErrNoPermission.Wrap("not group owner") } @@ -1228,7 +1229,7 @@ func (s *groupServer) DismissGroup(ctx context.Context, req *pbgroup.DismissGrou MembersID: membersID, GroupType: string(group.GroupType), } - if err := CallbackDismissGroup(ctx, reqCall); err != nil { + if err := CallbackDismissGroup(ctx, s.config, reqCall); err != nil { return nil, err } @@ -1244,7 +1245,7 @@ func (s *groupServer) MuteGroupMember(ctx context.Context, req *pbgroup.MuteGrou if err := s.PopulateGroupMember(ctx, member); err != nil { return nil, err } - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { opMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx)) if err != nil { return nil, err @@ -1278,7 +1279,7 @@ func (s *groupServer) CancelMuteGroupMember(ctx context.Context, req *pbgroup.Ca if err := s.PopulateGroupMember(ctx, member); err != nil { return nil, err } - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, s.config) { opMember, err := s.db.TakeGroupMember(ctx, req.GroupID, mcontext.GetOpUserID(ctx)) if err != nil { return nil, err @@ -1337,7 +1338,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr if opUserID == "" { return nil, errs.ErrNoPermission.Wrap("no op user id") } - isAppManagerUid := authverify.IsAppManagerUid(ctx) + isAppManagerUid := authverify.IsAppManagerUid(ctx, s.config) for i := range req.Members { req.Members[i].FaceURL = nil } @@ -1420,7 +1421,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr } } for i := 0; i < len(req.Members); i++ { - if err := CallbackBeforeSetGroupMemberInfo(ctx, req.Members[i]); err != nil { + if err := CallbackBeforeSetGroupMemberInfo(ctx, s.config, req.Members[i]); err != nil { return nil, err } } @@ -1447,7 +1448,7 @@ func (s *groupServer) SetGroupMemberInfo(ctx context.Context, req *pbgroup.SetGr } } for i := 0; i < len(req.Members); i++ { - if err := CallbackAfterSetGroupMemberInfo(ctx, req.Members[i]); err != nil { + if err := CallbackAfterSetGroupMemberInfo(ctx, s.config, req.Members[i]); err != nil { return nil, err } } diff --git a/internal/rpc/msg/as_read.go b/internal/rpc/msg/as_read.go index cac9102fa..f16fdc62f 100644 --- a/internal/rpc/msg/as_read.go +++ b/internal/rpc/msg/as_read.go @@ -129,7 +129,7 @@ func (m *msgServer) MarkMsgsAsRead( Seqs: req.Seqs, ContentType: conversation.ConversationType, } - if err = CallbackSingleMsgRead(ctx, req_callback); err != nil { + if err = CallbackSingleMsgRead(ctx, m.config, req_callback); err != nil { return nil, err } @@ -206,7 +206,7 @@ func (m *msgServer) MarkConversationAsRead( UnreadMsgNum: req.HasReadSeq, ContentType: int64(conversation.ConversationType), } - if err := CallbackGroupMsgRead(ctx, reqCall); err != nil { + if err := CallbackGroupMsgRead(ctx, m.config, reqCall); err != nil { return nil, err } diff --git a/internal/rpc/msg/callback.go b/internal/rpc/msg/callback.go index 1a7cad70c..536402bf9 100644 --- a/internal/rpc/msg/callback.go +++ b/internal/rpc/msg/callback.go @@ -29,10 +29,6 @@ import ( "google.golang.org/protobuf/proto" ) -func cbURL() string { - return config.Config.Callback.CallbackUrl -} - func toCommonCallback(ctx context.Context, msg *pbchat.SendMsgReq, command string) cbapi.CommonCallbackReq { return cbapi.CommonCallbackReq{ SendID: msg.MsgData.SendID, @@ -66,8 +62,8 @@ func GetContent(msg *sdkws.MsgData) string { } } -func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { +func callbackBeforeSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error { + if !globalConfig.Callback.CallbackBeforeSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackBeforeSendSingleMsgReq{ @@ -75,14 +71,14 @@ func callbackBeforeSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) er RecvID: msg.MsgData.RecvID, } resp := &cbapi.CallbackBeforeSendSingleMsgResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackBeforeSendSingleMsg); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendSingleMsg); err != nil { return err } return nil } -func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { +func callbackAfterSendSingleMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error { + if !globalConfig.Callback.CallbackAfterSendSingleMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackAfterSendSingleMsgReq{ @@ -90,14 +86,14 @@ func callbackAfterSendSingleMsg(ctx context.Context, msg *pbchat.SendMsgReq) err RecvID: msg.MsgData.RecvID, } resp := &cbapi.CallbackAfterSendSingleMsgResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendSingleMsg); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendSingleMsg); err != nil { return err } return nil } -func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { +func callbackBeforeSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error { + if !globalConfig.Callback.CallbackBeforeSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackBeforeSendGroupMsgReq{ @@ -105,14 +101,14 @@ func callbackBeforeSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) err GroupID: msg.MsgData.GroupID, } resp := &cbapi.CallbackBeforeSendGroupMsgResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackBeforeSendGroupMsg); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackBeforeSendGroupMsg); err != nil { return err } return nil } -func callbackAfterSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { +func callbackAfterSendGroupMsg(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error { + if !globalConfig.Callback.CallbackAfterSendGroupMsg.Enable || msg.MsgData.ContentType == constant.Typing { return nil } req := &cbapi.CallbackAfterSendGroupMsgReq{ @@ -120,21 +116,21 @@ func callbackAfterSendGroupMsg(ctx context.Context, msg *pbchat.SendMsgReq) erro GroupID: msg.MsgData.GroupID, } resp := &cbapi.CallbackAfterSendGroupMsgResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackAfterSendGroupMsg); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackAfterSendGroupMsg); err != nil { return err } return nil } -func callbackMsgModify(ctx context.Context, msg *pbchat.SendMsgReq) error { - if !config.Config.Callback.CallbackMsgModify.Enable || msg.MsgData.ContentType != constant.Text { +func callbackMsgModify(ctx context.Context, globalConfig *config.GlobalConfig, msg *pbchat.SendMsgReq) error { + if !globalConfig.Callback.CallbackMsgModify.Enable || msg.MsgData.ContentType != constant.Text { return nil } req := &cbapi.CallbackMsgModifyCommandReq{ CommonCallbackReq: toCommonCallback(ctx, msg, cbapi.CallbackMsgModifyCommand), } resp := &cbapi.CallbackMsgModifyCommandResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackMsgModify); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil { return err } if resp.Content != nil { @@ -159,34 +155,34 @@ func callbackMsgModify(ctx context.Context, msg *pbchat.SendMsgReq) error { log.ZDebug(ctx, "callbackMsgModify", "msg", msg.MsgData) return nil } -func CallbackGroupMsgRead(ctx context.Context, req *cbapi.CallbackGroupMsgReadReq) error { - if !config.Config.Callback.CallbackGroupMsgRead.Enable || req.ContentType != constant.Text { +func CallbackGroupMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackGroupMsgReadReq) error { + if !globalConfig.Callback.CallbackGroupMsgRead.Enable || req.ContentType != constant.Text { return nil } req.CallbackCommand = cbapi.CallbackGroupMsgReadCommand resp := &cbapi.CallbackGroupMsgReadResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackMsgModify); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil { return err } return nil } -func CallbackSingleMsgRead(ctx context.Context, req *cbapi.CallbackSingleMsgReadReq) error { - if !config.Config.Callback.CallbackSingleMsgRead.Enable || req.ContentType != constant.Text { +func CallbackSingleMsgRead(ctx context.Context, globalConfig *config.GlobalConfig, req *cbapi.CallbackSingleMsgReadReq) error { + if !globalConfig.Callback.CallbackSingleMsgRead.Enable || req.ContentType != constant.Text { return nil } req.CallbackCommand = cbapi.CallbackSingleMsgRead resp := &cbapi.CallbackSingleMsgReadResp{} - if err := http.CallBackPostReturn(ctx, cbURL(), req, resp, config.Config.Callback.CallbackMsgModify); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, req, resp, globalConfig.Callback.CallbackMsgModify); err != nil { return err } return nil } -func CallbackAfterRevokeMsg(ctx context.Context, req *pbchat.RevokeMsgReq) error { - if !config.Config.Callback.CallbackAfterRevokeMsg.Enable { +func CallbackAfterRevokeMsg(ctx context.Context, globalConfig *config.GlobalConfig, req *pbchat.RevokeMsgReq) error { + if !globalConfig.Callback.CallbackAfterRevokeMsg.Enable { return nil } callbackReq := &cbapi.CallbackAfterRevokeMsgReq{ @@ -196,7 +192,7 @@ func CallbackAfterRevokeMsg(ctx context.Context, req *pbchat.RevokeMsgReq) error UserID: req.UserID, } resp := &cbapi.CallbackAfterRevokeMsgResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, callbackReq, resp, config.Config.Callback.CallbackAfterRevokeMsg); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, callbackReq, resp, globalConfig.Callback.CallbackAfterRevokeMsg); err != nil { return err } return nil diff --git a/internal/rpc/msg/delete.go b/internal/rpc/msg/delete.go index 091f11f4c..14e24d23e 100644 --- a/internal/rpc/msg/delete.go +++ b/internal/rpc/msg/delete.go @@ -45,7 +45,7 @@ func (m *msgServer) ClearConversationsMsg( ctx context.Context, req *msg.ClearConversationsMsgReq, ) (*msg.ClearConversationsMsgResp, error) { - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil { return nil, err } if err := m.clearConversation(ctx, req.ConversationIDs, req.UserID, req.DeleteSyncOpt); err != nil { @@ -58,7 +58,7 @@ func (m *msgServer) UserClearAllMsg( ctx context.Context, req *msg.UserClearAllMsgReq, ) (*msg.UserClearAllMsgResp, error) { - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil { return nil, err } conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID) @@ -73,7 +73,7 @@ func (m *msgServer) UserClearAllMsg( } func (m *msgServer) DeleteMsgs(ctx context.Context, req *msg.DeleteMsgsReq) (*msg.DeleteMsgsResp, error) { - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil { return nil, err } isSyncSelf, isSyncOther := m.validateDeleteSyncOpt(req.DeleteSyncOpt) @@ -121,7 +121,7 @@ func (m *msgServer) DeleteMsgPhysical( ctx context.Context, req *msg.DeleteMsgPhysicalReq, ) (*msg.DeleteMsgPhysicalResp, error) { - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, m.config); err != nil { return nil, err } remainTime := utils.GetCurrentTimestampBySecond() - req.Timestamp diff --git a/internal/rpc/msg/message_interceptor.go b/internal/rpc/msg/message_interceptor.go index 3a2731fea..97eac613d 100644 --- a/internal/rpc/msg/message_interceptor.go +++ b/internal/rpc/msg/message_interceptor.go @@ -24,17 +24,17 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) -type MessageInterceptorFunc func(ctx context.Context, req *msg.SendMsgReq) (*sdkws.MsgData, error) +type MessageInterceptorFunc func(ctx context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error) -func MessageHasReadEnabled(_ context.Context, req *msg.SendMsgReq) (*sdkws.MsgData, error) { +func MessageHasReadEnabled(_ context.Context, globalConfig *config.GlobalConfig, req *msg.SendMsgReq) (*sdkws.MsgData, error) { switch { case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SingleChatType: - if !config.Config.SingleMessageHasReadReceiptEnable { + if !globalConfig.SingleMessageHasReadReceiptEnable { return nil, errs.ErrMessageHasReadDisable.Wrap() } return req.MsgData, nil case req.MsgData.ContentType == constant.HasReadReceipt && req.MsgData.SessionType == constant.SuperGroupChatType: - if !config.Config.GroupMessageHasReadReceiptEnable { + if !globalConfig.GroupMessageHasReadReceiptEnable { return nil, errs.ErrMessageHasReadDisable.Wrap() } return req.MsgData, nil diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index eebfdf779..4f844369f 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -19,6 +19,8 @@ import ( "encoding/json" "time" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" @@ -26,8 +28,7 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) @@ -42,7 +43,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. if req.Seq < 0 { return nil, errs.ErrArgs.Wrap("seq is invalid") } - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil { return nil, err } user, err := m.User.GetUserInfo(ctx, req.UserID) @@ -63,10 +64,10 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. data, _ := json.Marshal(msgs[0]) log.ZInfo(ctx, "GetMsgBySeqs", "conversationID", req.ConversationID, "seq", req.Seq, "msg", string(data)) var role int32 - if !authverify.IsAppManagerUid(ctx) { + if !authverify.IsAppManagerUid(ctx, m.config) { switch msgs[0].SessionType { case constant.SingleChatType: - if err := authverify.CheckAccessV3(ctx, msgs[0].SendID); err != nil { + if err := authverify.CheckAccessV3(ctx, msgs[0].SendID, m.config); err != nil { return nil, err } role = user.AppMangerLevel @@ -110,11 +111,11 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. } revokerUserID := mcontext.GetOpUserID(ctx) var flag bool - if len(config.Config.Manager.UserID) > 0 { - flag = utils.Contain(revokerUserID, config.Config.Manager.UserID...) + if len(m.config.Manager.UserID) > 0 { + flag = utils.Contain(revokerUserID, m.config.Manager.UserID...) } - if len(config.Config.Manager.UserID) == 0 && len(config.Config.IMAdmin.UserID) > 0 { - flag = utils.Contain(revokerUserID, config.Config.IMAdmin.UserID...) + if len(m.config.Manager.UserID) == 0 && len(m.config.IMAdmin.UserID) > 0 { + flag = utils.Contain(revokerUserID, m.config.IMAdmin.UserID...) } tips := sdkws.RevokeMsgTips{ RevokerUserID: revokerUserID, @@ -134,7 +135,7 @@ func (m *msgServer) RevokeMsg(ctx context.Context, req *msg.RevokeMsgReq) (*msg. if err := m.notificationSender.NotificationWithSesstionType(ctx, req.UserID, recvID, constant.MsgRevokeNotification, msgs[0].SessionType, &tips); err != nil { return nil, err } - if err = CallbackAfterRevokeMsg(ctx, req); err != nil { + if err = CallbackAfterRevokeMsg(ctx, m.config, req); err != nil { return nil, err } return &msg.RevokeMsgResp{}, nil diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index e04cebb3b..4bac4d1e0 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -17,6 +17,9 @@ package msg import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" + "github.com/OpenIMSDK/protocol/constant" pbconversation "github.com/OpenIMSDK/protocol/conversation" pbmsg "github.com/OpenIMSDK/protocol/msg" @@ -26,14 +29,12 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, error error) { resp = &pbmsg.SendMsgResp{} if req.MsgData != nil { - flag := isMessageHasReadEnabled(req.MsgData) + flag := isMessageHasReadEnabled(req.MsgData, m.config) if !flag { return nil, errs.ErrMessageHasReadDisable.Wrap() } @@ -61,11 +62,11 @@ func (m *msgServer) sendMsgSuperGroupChat( prommetrics.GroupChatMsgProcessFailedCounter.Inc() return nil, err } - if err = callbackBeforeSendGroupMsg(ctx, req); err != nil { + if err = callbackBeforeSendGroupMsg(ctx, m.config, req); err != nil { return nil, err } - if err := callbackMsgModify(ctx, req); err != nil { + if err := callbackMsgModify(ctx, m.config, req); err != nil { return nil, err } err = m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForGroup(req.MsgData.GroupID), req.MsgData) @@ -75,7 +76,7 @@ func (m *msgServer) sendMsgSuperGroupChat( if req.MsgData.ContentType == constant.AtText { go m.setConversationAtInfo(ctx, req.MsgData) } - if err = callbackAfterSendGroupMsg(ctx, req); err != nil { + if err = callbackAfterSendGroupMsg(ctx, m.config, req); err != nil { log.ZWarn(ctx, "CallbackAfterSendGroupMsg", err) } prommetrics.GroupChatMsgProcessSuccessCounter.Inc() @@ -107,7 +108,7 @@ func (m *msgServer) setConversationAtInfo(nctx context.Context, msg *sdkws.MsgDa conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAll} } else { //@Everyone and @other people conversation.GroupAtType = &wrapperspb.Int32Value{Value: constant.AtAllAtMe} - err := m.Conversation.SetConversations(ctx, atUserID, conversation) + err = m.Conversation.SetConversations(ctx, atUserID, conversation) if err != nil { log.ZWarn(ctx, "SetConversations", err, "userID", atUserID, "conversation", conversation) } @@ -164,18 +165,18 @@ func (m *msgServer) sendMsgSingleChat(ctx context.Context, req *pbmsg.SendMsgReq prommetrics.SingleChatMsgProcessFailedCounter.Inc() return nil, nil } else { - if err = callbackBeforeSendSingleMsg(ctx, req); err != nil { + if err = callbackBeforeSendSingleMsg(ctx, m.config, req); err != nil { return nil, err } - if err := callbackMsgModify(ctx, req); err != nil { + if err := callbackMsgModify(ctx, m.config, req); err != nil { return nil, err } if err := m.MsgDatabase.MsgToMQ(ctx, utils.GenConversationUniqueKeyForSingle(req.MsgData.SendID, req.MsgData.RecvID), req.MsgData); err != nil { prommetrics.SingleChatMsgProcessFailedCounter.Inc() return nil, err } - err = callbackAfterSendSingleMsg(ctx, req) + err = callbackAfterSendSingleMsg(ctx, m.config, req) if err != nil { log.ZWarn(ctx, "CallbackAfterSendSingleMsg", err, "req", req) } diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 79ae483a2..5b7cd2f66 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,16 +15,20 @@ package msg import ( + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + + "google.golang.org/grpc" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - "google.golang.org/grpc" ) type ( @@ -40,6 +44,7 @@ type ( ConversationLocalCache *localcache.ConversationLocalCache Handlers MessageInterceptorChain notificationSender *rpcclient.NotificationSender + config *config.GlobalConfig } ) @@ -47,37 +52,36 @@ func (m *msgServer) addInterceptorHandler(interceptorFunc ...MessageInterceptorF m.Handlers = append(m.Handlers, interceptorFunc...) } -// func `(*msgServer).execInterceptorHandler` is unused -// func (m *msgServer) execInterceptorHandler(ctx context.Context, req *msg.SendMsgReq) error { -// for _, handler := range m.Handlers { -// msgData, err := handler(ctx, req) -// if err != nil { -// return err -// } -// req.MsgData = msgData -// } -// return nil -// } +//func (m *msgServer) execInterceptorHandler(ctx context.Context, config *config.GlobalConfig, req *msg.SendMsgReq) error { +// for _, handler := range m.Handlers { +// msgData, err := handler(ctx, config, req) +// if err != nil { +// return err +// } +// req.MsgData = msgData +// } +// return nil +//} -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return err } if err := mongo.CreateMsgIndex(); err != nil { return err } - cacheModel := cache.NewMsgCacheModel(rdb) - msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase()) - conversationClient := rpcclient.NewConversationRpcClient(client) - userRpcClient := rpcclient.NewUserRpcClient(client) - groupRpcClient := rpcclient.NewGroupRpcClient(client) - friendRpcClient := rpcclient.NewFriendRpcClient(client) - msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel) + cacheModel := cache.NewMsgCacheModel(rdb, config) + msgDocModel := unrelation.NewMsgMongoDriver(mongo.GetDatabase(config.Mongo.Database)) + conversationClient := rpcclient.NewConversationRpcClient(client, config) + userRpcClient := rpcclient.NewUserRpcClient(client, config) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + friendRpcClient := rpcclient.NewFriendRpcClient(client, config) + msgDatabase, err := controller.NewCommonMsgDatabase(msgDocModel, cacheModel, config) if err != nil { return err } @@ -90,8 +94,9 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e GroupLocalCache: localcache.NewGroupLocalCache(&groupRpcClient), ConversationLocalCache: localcache.NewConversationLocalCache(&conversationClient), friend: &friendRpcClient, + config: config, } - s.notificationSender = rpcclient.NewNotificationSender(rpcclient.WithLocalSendMsg(s.SendMsg)) + s.notificationSender = rpcclient.NewNotificationSender(config, rpcclient.WithLocalSendMsg(s.SendMsg)) s.addInterceptorHandler(MessageHasReadEnabled) msg.RegisterMsgServer(server, s) return nil diff --git a/internal/rpc/msg/sync_msg.go b/internal/rpc/msg/sync_msg.go index fa894e034..b714da375 100644 --- a/internal/rpc/msg/sync_msg.go +++ b/internal/rpc/msg/sync_msg.go @@ -88,7 +88,7 @@ func (m *msgServer) PullMessageBySeqs( } func (m *msgServer) GetMaxSeq(ctx context.Context, req *sdkws.GetMaxSeqReq) (*sdkws.GetMaxSeqResp, error) { - if err := authverify.CheckAccessV3(ctx, req.UserID); err != nil { + if err := authverify.CheckAccessV3(ctx, req.UserID, m.config); err != nil { return nil, err } conversationIDs, err := m.ConversationLocalCache.GetConversationIDs(ctx, req.UserID) diff --git a/internal/rpc/msg/utils.go b/internal/rpc/msg/utils.go index d8a45e875..48665562a 100644 --- a/internal/rpc/msg/utils.go +++ b/internal/rpc/msg/utils.go @@ -23,16 +23,16 @@ import ( "go.mongodb.org/mongo-driver/mongo" ) -func isMessageHasReadEnabled(msgData *sdkws.MsgData) bool { +func isMessageHasReadEnabled(msgData *sdkws.MsgData, config *config.GlobalConfig) bool { switch { case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SingleChatType: - if config.Config.SingleMessageHasReadReceiptEnable { + if config.SingleMessageHasReadReceiptEnable { return true } else { return false } case msgData.ContentType == constant.HasReadReceipt && msgData.SessionType == constant.SuperGroupChatType: - if config.Config.GroupMessageHasReadReceiptEnable { + if config.GroupMessageHasReadReceiptEnable { return true } else { return false diff --git a/internal/rpc/msg/verify.go b/internal/rpc/msg/verify.go index 11055fac1..d72e4923e 100644 --- a/internal/rpc/msg/verify.go +++ b/internal/rpc/msg/verify.go @@ -25,7 +25,6 @@ import ( "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) var ExcludeContentType = []int{constant.HasReadReceipt} @@ -50,10 +49,10 @@ type MessageRevoked struct { func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgReq) error { switch data.MsgData.SessionType { case constant.SingleChatType: - if len(config.Config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, config.Config.Manager.UserID) { + if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) { return nil } - if utils.IsContain(data.MsgData.SendID, config.Config.IMAdmin.UserID) { + if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) { return nil } if data.MsgData.ContentType <= constant.NotificationEnd && @@ -67,7 +66,7 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if black { return errs.ErrBlockedByPeer.Wrap() } - if *config.Config.MessageVerify.FriendVerify { + if *m.config.MessageVerify.FriendVerify { friend, err := m.friend.IsFriend(ctx, data.MsgData.SendID, data.MsgData.RecvID) if err != nil { return err @@ -90,10 +89,10 @@ func (m *msgServer) messageVerification(ctx context.Context, data *msg.SendMsgRe if groupInfo.GroupType == constant.SuperGroup { return nil } - if len(config.Config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, config.Config.Manager.UserID) { + if len(m.config.Manager.UserID) > 0 && utils.IsContain(data.MsgData.SendID, m.config.Manager.UserID) { return nil } - if utils.IsContain(data.MsgData.SendID, config.Config.IMAdmin.UserID) { + if utils.IsContain(data.MsgData.SendID, m.config.IMAdmin.UserID) { return nil } if data.MsgData.ContentType <= constant.NotificationEnd && @@ -158,9 +157,6 @@ func (m *msgServer) encapsulateMsgData(msg *sdkws.MsgData) { case constant.Custom: fallthrough case constant.Quote: - utils.SetSwitchFromOptions(msg.Options, constant.IsConversationUpdate, true) - utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, true) - utils.SetSwitchFromOptions(msg.Options, constant.IsSenderSync, true) case constant.Revoke: utils.SetSwitchFromOptions(msg.Options, constant.IsUnreadCount, false) utils.SetSwitchFromOptions(msg.Options, constant.IsOfflinePush, false) diff --git a/internal/rpc/third/log.go b/internal/rpc/third/log.go index 420f399ba..b425dd819 100644 --- a/internal/rpc/third/log.go +++ b/internal/rpc/third/log.go @@ -82,7 +82,7 @@ func (t *thirdServer) UploadLogs(ctx context.Context, req *third.UploadLogsReq) } func (t *thirdServer) DeleteLogs(ctx context.Context, req *third.DeleteLogsReq) (*third.DeleteLogsResp, error) { - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, t.config); err != nil { return nil, err } userID := "" @@ -123,7 +123,7 @@ func dbToPbLogInfos(logs []*relationtb.LogModel) []*third.LogInfo { } func (t *thirdServer) SearchLogs(ctx context.Context, req *third.SearchLogsReq) (*third.SearchLogsResp, error) { - if err := authverify.CheckAdmin(ctx); err != nil { + if err := authverify.CheckAdmin(ctx, t.config); err != nil { return nil, err } var ( diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 7f68f4da8..1975163e5 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -29,7 +29,6 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" "github.com/google/uuid" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cont" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" @@ -54,7 +53,7 @@ func (t *thirdServer) PartSize(ctx context.Context, req *third.PartSizeReq) (*th func (t *thirdServer) InitiateMultipartUpload(ctx context.Context, req *third.InitiateMultipartUploadReq) (*third.InitiateMultipartUploadResp, error) { defer log.ZDebug(ctx, "return") - if err := checkUploadName(ctx, req.Name); err != nil { + if err := t.checkUploadName(ctx, req.Name); err != nil { return nil, err } expireTime := time.Now().Add(t.defaultExpire) @@ -133,7 +132,7 @@ func (t *thirdServer) AuthSign(ctx context.Context, req *third.AuthSignReq) (*th func (t *thirdServer) CompleteMultipartUpload(ctx context.Context, req *third.CompleteMultipartUploadReq) (*third.CompleteMultipartUploadResp, error) { defer log.ZDebug(ctx, "return") - if err := checkUploadName(ctx, req.Name); err != nil { + if err := t.checkUploadName(ctx, req.Name); err != nil { return nil, err } result, err := t.s3dataBase.CompleteMultipartUpload(ctx, req.UploadID, req.Parts) @@ -190,13 +189,13 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF if req.Size <= 0 { return nil, errs.ErrArgs.Wrap("size must be greater than 0") } - if err := checkUploadName(ctx, req.Name); err != nil { + if err := t.checkUploadName(ctx, req.Name); err != nil { return nil, err } var duration time.Duration opUserID := mcontext.GetOpUserID(ctx) var key string - if authverify.IsManagerUserID(opUserID) { + if t.IsManagerUserID(opUserID) { if req.Millisecond <= 0 { duration = time.Minute * 10 } else { @@ -256,7 +255,7 @@ func (t *thirdServer) CompleteFormData(ctx context.Context, req *third.CompleteF if err := json.Unmarshal(data, &mate); err != nil { return nil, errs.ErrArgs.Wrap("invalid id " + err.Error()) } - if err := checkUploadName(ctx, mate.Name); err != nil { + if err := t.checkUploadName(ctx, mate.Name); err != nil { return nil, err } info, err := t.s3dataBase.StatObject(ctx, mate.Key) diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 9dd3ffd65..2bccb5c78 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -20,59 +20,63 @@ import ( "net/url" "time" - "github.com/OpenIMSDK/protocol/third" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" + + "github.com/OpenIMSDK/protocol/third" + "github.com/OpenIMSDK/tools/discoveryregistry" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) -func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { - mongo, err := unrelation.NewMongo() +func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { + mongo, err := unrelation.NewMongo(config) if err != nil { return err } - logdb, err := mgo.NewLogMongo(mongo.GetDatabase()) + logdb, err := mgo.NewLogMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - s3db, err := mgo.NewS3Mongo(mongo.GetDatabase()) + s3db, err := mgo.NewS3Mongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } - apiURL := config.Config.Object.ApiURL + apiURL := config.Object.ApiURL if apiURL == "" { return fmt.Errorf("api url is empty") } - if _, err := url.Parse(config.Config.Object.ApiURL); err != nil { + if _, parseErr := url.Parse(config.Object.ApiURL); parseErr != nil { return err } if apiURL[len(apiURL)-1] != '/' { apiURL += "/" } apiURL += "object/" - rdb, err := cache.NewRedis() + rdb, err := cache.NewRedis(config) if err != nil { return err } - // Select based on the configuration file strategy - enable := config.Config.Object.Enable + // 根据配置文件策略选择 oss 方式 + enable := config.Object.Enable var o s3.Interface - switch config.Config.Object.Enable { + switch enable { case "minio": - o, err = minio.NewMinio(cache.NewMinioCache(rdb)) + o, err = minio.NewMinio(cache.NewMinioCache(rdb), minio.Config(config.Object.Minio)) case "cos": - o, err = cos.NewCos() + o, err = cos.NewCos(cos.Config(config.Object.Cos)) case "oss": - o, err = oss.NewOSS() + o, err = oss.NewOSS(oss.Config(config.Object.Oss)) default: err = fmt.Errorf("invalid object enable: %s", enable) } @@ -81,10 +85,11 @@ func Start(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) e } third.RegisterThirdServer(server, &thirdServer{ apiURL: apiURL, - thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb), logdb), - userRpcClient: rpcclient.NewUserRpcClient(client), + thirdDatabase: controller.NewThirdDatabase(cache.NewMsgCacheModel(rdb, config), logdb), + userRpcClient: rpcclient.NewUserRpcClient(client, config), s3dataBase: controller.NewS3Database(rdb, o, s3db), defaultExpire: time.Hour * 24 * 7, + config: config, }) return nil } @@ -95,6 +100,7 @@ type thirdServer struct { s3dataBase controller.S3Database userRpcClient rpcclient.UserRpcClient defaultExpire time.Duration + config *config.GlobalConfig } func (t *thirdServer) FcmUpdateToken(ctx context.Context, req *third.FcmUpdateTokenReq) (resp *third.FcmUpdateTokenResp, err error) { diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go index cf25f9820..6591134d6 100644 --- a/internal/rpc/third/tool.go +++ b/internal/rpc/third/tool.go @@ -21,10 +21,11 @@ import ( "strings" "unicode/utf8" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) func toPbMapArray(m map[string][]string) []*third.KeyValues { @@ -41,7 +42,7 @@ func toPbMapArray(m map[string][]string) []*third.KeyValues { return res } -func checkUploadName(ctx context.Context, name string) error { +func (t *thirdServer) checkUploadName(ctx context.Context, name string) error { if name == "" { return errs.ErrArgs.Wrap("name is empty") } @@ -55,7 +56,7 @@ func checkUploadName(ctx context.Context, name string) error { if opUserID == "" { return errs.ErrNoPermission.Wrap("opUserID is empty") } - if !authverify.IsManagerUserID(opUserID) { + if !authverify.IsManagerUserID(opUserID, t.config) { if !strings.HasPrefix(name, opUserID+"/") { return errs.ErrNoPermission.Wrap(fmt.Sprintf("name must start with `%s/`", opUserID)) } @@ -79,3 +80,7 @@ func checkValidObjectName(objectName string) error { } return checkValidObjectNamePrefix(objectName) } + +func (t *thirdServer) IsManagerUserID(opUserID string) bool { + return authverify.IsManagerUserID(opUserID, t.config) +} diff --git a/internal/rpc/user/callback.go b/internal/rpc/user/callback.go index 1437257f7..34f211973 100644 --- a/internal/rpc/user/callback.go +++ b/internal/rpc/user/callback.go @@ -24,8 +24,8 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/http" ) -func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error { - if !config.Config.Callback.CallbackBeforeUpdateUserInfo.Enable { +func CallbackBeforeUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error { + if !globalConfig.Callback.CallbackBeforeUpdateUserInfo.Enable { return nil } cbReq := &cbapi.CallbackBeforeUpdateUserInfoReq{ @@ -35,7 +35,7 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInf Nickname: &req.UserInfo.Nickname, } resp := &cbapi.CallbackBeforeUpdateUserInfoResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil { return err } utils.NotNilReplace(&req.UserInfo.FaceURL, resp.FaceURL) @@ -43,8 +43,8 @@ func CallbackBeforeUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInf utils.NotNilReplace(&req.UserInfo.Nickname, resp.Nickname) return nil } -func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) error { - if !config.Config.Callback.CallbackAfterUpdateUserInfo.Enable { +func CallbackAfterUpdateUserInfo(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoReq) error { + if !globalConfig.Callback.CallbackAfterUpdateUserInfo.Enable { return nil } cbReq := &cbapi.CallbackAfterUpdateUserInfoReq{ @@ -54,13 +54,13 @@ func CallbackAfterUpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfo Nickname: req.UserInfo.Nickname, } resp := &cbapi.CallbackAfterUpdateUserInfoResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil { return err } return nil } -func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error { - if !config.Config.Callback.CallbackBeforeUpdateUserInfoEx.Enable { +func CallbackBeforeUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error { + if !globalConfig.Callback.CallbackBeforeUpdateUserInfoEx.Enable { return nil } cbReq := &cbapi.CallbackBeforeUpdateUserInfoExReq{ @@ -70,7 +70,7 @@ func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserI Nickname: req.UserInfo.Nickname, } resp := &cbapi.CallbackBeforeUpdateUserInfoExResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { return err } utils.NotNilReplace(req.UserInfo.FaceURL, resp.FaceURL) @@ -78,8 +78,8 @@ func CallbackBeforeUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserI utils.NotNilReplace(req.UserInfo.Nickname, resp.Nickname) return nil } -func CallbackAfterUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) error { - if !config.Config.Callback.CallbackAfterUpdateUserInfoEx.Enable { +func CallbackAfterUpdateUserInfoEx(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UpdateUserInfoExReq) error { + if !globalConfig.Callback.CallbackAfterUpdateUserInfoEx.Enable { return nil } cbReq := &cbapi.CallbackAfterUpdateUserInfoExReq{ @@ -89,14 +89,14 @@ func CallbackAfterUpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserIn Nickname: req.UserInfo.Nickname, } resp := &cbapi.CallbackAfterUpdateUserInfoExResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfoEx); err != nil { return err } return nil } -func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) error { - if !config.Config.Callback.CallbackBeforeUserRegister.Enable { +func CallbackBeforeUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error { + if !globalConfig.Callback.CallbackBeforeUserRegister.Enable { return nil } cbReq := &cbapi.CallbackBeforeUserRegisterReq{ @@ -106,7 +106,7 @@ func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq } resp := &cbapi.CallbackBeforeUserRegisterResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackBeforeUpdateUserInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackBeforeUpdateUserInfo); err != nil { return err } if len(resp.Users) != 0 { @@ -115,8 +115,8 @@ func CallbackBeforeUserRegister(ctx context.Context, req *pbuser.UserRegisterReq return nil } -func CallbackAfterUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) error { - if !config.Config.Callback.CallbackAfterUserRegister.Enable { +func CallbackAfterUserRegister(ctx context.Context, globalConfig *config.GlobalConfig, req *pbuser.UserRegisterReq) error { + if !globalConfig.Callback.CallbackAfterUserRegister.Enable { return nil } cbReq := &cbapi.CallbackAfterUserRegisterReq{ @@ -126,7 +126,7 @@ func CallbackAfterUserRegister(ctx context.Context, req *pbuser.UserRegisterReq) } resp := &cbapi.CallbackAfterUserRegisterResp{} - if err := http.CallBackPostReturn(ctx, config.Config.Callback.CallbackUrl, cbReq, resp, config.Config.Callback.CallbackAfterUpdateUserInfo); err != nil { + if err := http.CallBackPostReturn(ctx, globalConfig.Callback.CallbackUrl, cbReq, resp, globalConfig.Callback.CallbackAfterUpdateUserInfo); err != nil { return err } return nil diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 7ed3ff7d6..02e641d20 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -21,26 +21,34 @@ import ( "strings" "time" + "github.com/OpenIMSDK/tools/pagination" + + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + + "github.com/OpenIMSDK/tools/tx" + + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" - pbuser "github.com/OpenIMSDK/protocol/user" - registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/pagination" - "github.com/OpenIMSDK/tools/tx" - "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + + registry "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + + pbuser "github.com/OpenIMSDK/protocol/user" + "github.com/OpenIMSDK/tools/utils" "google.golang.org/grpc" ) @@ -51,6 +59,7 @@ type userServer struct { friendRpcClient *rpcclient.FriendRpcClient groupRpcClient *rpcclient.GroupRpcClient RegisterCenter registry.SvcDiscoveryRegistry + config *config.GlobalConfig } func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGroupOnlineUserReq) (*pbuser.GetGroupOnlineUserResp, error) { @@ -58,39 +67,40 @@ func (s *userServer) GetGroupOnlineUser(ctx context.Context, req *pbuser.GetGrou panic("implement me") } -func Start(client registry.SvcDiscoveryRegistry, server *grpc.Server) error { - rdb, err := cache.NewRedis() +func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, server *grpc.Server) error { + rdb, err := cache.NewRedis(config) if err != nil { return err } - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return err } users := make([]*tablerelation.UserModel, 0) - if len(config.Config.IMAdmin.UserID) != len(config.Config.IMAdmin.Nickname) { - return errors.New("len(config.Config.AppNotificationAdmin.AppManagerUid) != len(config.Config.AppNotificationAdmin.Nickname)") + if len(config.IMAdmin.UserID) != len(config.IMAdmin.Nickname) { + return errors.New("len(s.config.AppNotificationAdmin.AppManagerUid) != len(s.config.AppNotificationAdmin.Nickname)") } - for k, v := range config.Config.IMAdmin.UserID { - users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.Config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin}) + for k, v := range config.IMAdmin.UserID { + users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin}) } - userDB, err := mgo.NewUserMongo(mongo.GetDatabase()) + userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return err } cache := cache.NewUserCacheRedis(rdb, userDB, cache.GetDefaultOpt()) - userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase()) + userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database)) database := controller.NewUserDatabase(userDB, cache, tx.NewMongo(mongo.GetClient()), userMongoDB) - friendRpcClient := rpcclient.NewFriendRpcClient(client) - groupRpcClient := rpcclient.NewGroupRpcClient(client) - msgRpcClient := rpcclient.NewMessageRpcClient(client) + friendRpcClient := rpcclient.NewFriendRpcClient(client, config) + groupRpcClient := rpcclient.NewGroupRpcClient(client, config) + msgRpcClient := rpcclient.NewMessageRpcClient(client, config) u := &userServer{ UserDatabase: database, RegisterCenter: client, friendRpcClient: &friendRpcClient, groupRpcClient: &groupRpcClient, - friendNotificationSender: notification.NewFriendNotificationSender(&msgRpcClient, notification.WithDBFunc(database.FindWithError)), - userNotificationSender: notification.NewUserNotificationSender(&msgRpcClient, notification.WithUserFunc(database.FindWithError)), + friendNotificationSender: notification.NewFriendNotificationSender(config, &msgRpcClient, notification.WithDBFunc(database.FindWithError)), + userNotificationSender: notification.NewUserNotificationSender(config, &msgRpcClient, notification.WithUserFunc(database.FindWithError)), + config: config, } pbuser.RegisterUserServer(server, u) return u.UserDatabase.InitOnce(context.Background(), users) @@ -111,11 +121,11 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserInfoReq) (resp *pbuser.UpdateUserInfoResp, err error) { resp = &pbuser.UpdateUserInfoResp{} - err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID) + err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config) if err != nil { return nil, err } - if err := CallbackBeforeUpdateUserInfo(ctx, req); err != nil { + if err := CallbackBeforeUpdateUserInfo(ctx, s.config, req); err != nil { return nil, err } data := convert.UserPb2DBMap(req.UserInfo) @@ -128,29 +138,29 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI return nil, err } if req.UserInfo.Nickname != "" || req.UserInfo.FaceURL != "" { - if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { + if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { log.ZError(ctx, "NotificationUserInfoUpdate", err) } } for _, friendID := range friends { s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID) } - if err := CallbackAfterUpdateUserInfo(ctx, req); err != nil { + if err = CallbackAfterUpdateUserInfo(ctx, s.config, req); err != nil { return nil, err } - if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { + if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { log.ZError(ctx, "NotificationUserInfoUpdate", err, "userID", req.UserInfo.UserID) } return resp, nil } func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUserInfoExReq) (resp *pbuser.UpdateUserInfoExResp, err error) { resp = &pbuser.UpdateUserInfoExResp{} - err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID) + err = authverify.CheckAccessV3(ctx, req.UserInfo.UserID, s.config) if err != nil { return nil, err } - if err = CallbackBeforeUpdateUserInfoEx(ctx, req); err != nil { + if err = CallbackBeforeUpdateUserInfoEx(ctx, s.config, req); err != nil { return nil, err } data := convert.UserPb2DBMapEx(req.UserInfo) @@ -170,7 +180,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse for _, friendID := range friends { s.friendNotificationSender.FriendInfoUpdatedNotification(ctx, req.UserInfo.UserID, friendID) } - if err := CallbackAfterUpdateUserInfoEx(ctx, req); err != nil { + if err := CallbackAfterUpdateUserInfoEx(ctx, s.config, req); err != nil { return nil, err } if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { @@ -197,7 +207,7 @@ func (s *userServer) AccountCheck(ctx context.Context, req *pbuser.AccountCheckR if utils.Duplicate(req.CheckUserIDs) { return nil, errs.ErrArgs.Wrap("userID repeated") } - err = authverify.CheckAdmin(ctx) + err = authverify.CheckAdmin(ctx, s.config) if err != nil { return nil, err } @@ -244,8 +254,8 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR if len(req.Users) == 0 { return nil, errs.ErrArgs.Wrap("users is empty") } - if req.Secret != config.Config.Secret { - log.ZDebug(ctx, "UserRegister", config.Config.Secret, req.Secret) + if req.Secret != s.config.Secret { + log.ZDebug(ctx, "UserRegister", s.config.Secret, req.Secret) return nil, errs.ErrNoPermission.Wrap("secret invalid") } if utils.DuplicateAny(req.Users, func(e *sdkws.UserInfo) string { return e.UserID }) { @@ -268,7 +278,7 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR if exist { return nil, errs.ErrRegisteredAlready.Wrap("userID registered already") } - if err := CallbackBeforeUserRegister(ctx, req); err != nil { + if err := CallbackBeforeUserRegister(ctx, s.config, req); err != nil { return nil, err } now := time.Now() @@ -288,7 +298,7 @@ func (s *userServer) UserRegister(ctx context.Context, req *pbuser.UserRegisterR return nil, err } - if err := CallbackAfterUserRegister(ctx, req); err != nil { + if err := CallbackAfterUserRegister(ctx, s.config, req); err != nil { return nil, err } return resp, nil @@ -383,7 +393,7 @@ func (s *userServer) GetSubscribeUsersStatus(ctx context.Context, // ProcessUserCommandAdd user general function add. func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.ProcessUserCommandAddReq) (*pbuser.ProcessUserCommandAddResp, error) { - err := authverify.CheckAccessV3(ctx, req.UserID) + err := authverify.CheckAccessV3(ctx, req.UserID, s.config) if err != nil { return nil, err } @@ -414,7 +424,7 @@ func (s *userServer) ProcessUserCommandAdd(ctx context.Context, req *pbuser.Proc // ProcessUserCommandDelete user general function delete. func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.ProcessUserCommandDeleteReq) (*pbuser.ProcessUserCommandDeleteResp, error) { - err := authverify.CheckAccessV3(ctx, req.UserID) + err := authverify.CheckAccessV3(ctx, req.UserID, s.config) if err != nil { return nil, err } @@ -437,7 +447,7 @@ func (s *userServer) ProcessUserCommandDelete(ctx context.Context, req *pbuser.P // ProcessUserCommandUpdate user general function update. func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.ProcessUserCommandUpdateReq) (*pbuser.ProcessUserCommandUpdateResp, error) { - err := authverify.CheckAccessV3(ctx, req.UserID) + err := authverify.CheckAccessV3(ctx, req.UserID, s.config) if err != nil { return nil, err } @@ -469,7 +479,7 @@ func (s *userServer) ProcessUserCommandUpdate(ctx context.Context, req *pbuser.P func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.ProcessUserCommandGetReq) (*pbuser.ProcessUserCommandGetResp, error) { - err := authverify.CheckAccessV3(ctx, req.UserID) + err := authverify.CheckAccessV3(ctx, req.UserID, s.config) if err != nil { return nil, err } @@ -498,7 +508,7 @@ func (s *userServer) ProcessUserCommandGet(ctx context.Context, req *pbuser.Proc } func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.ProcessUserCommandGetAllReq) (*pbuser.ProcessUserCommandGetAllResp, error) { - err := authverify.CheckAccessV3(ctx, req.UserID) + err := authverify.CheckAccessV3(ctx, req.UserID, s.config) if err != nil { return nil, err } @@ -527,7 +537,7 @@ func (s *userServer) ProcessUserCommandGetAll(ctx context.Context, req *pbuser.P } func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.AddNotificationAccountReq) (*pbuser.AddNotificationAccountResp, error) { - if err := authverify.CheckIMAdmin(ctx); err != nil { + if err := authverify.CheckIMAdmin(ctx, s.config); err != nil { return nil, err } @@ -570,7 +580,7 @@ func (s *userServer) AddNotificationAccount(ctx context.Context, req *pbuser.Add } func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbuser.UpdateNotificationAccountInfoReq) (*pbuser.UpdateNotificationAccountInfoResp, error) { - if err := authverify.CheckIMAdmin(ctx); err != nil { + if err := authverify.CheckIMAdmin(ctx, s.config); err != nil { return nil, err } @@ -597,7 +607,7 @@ func (s *userServer) UpdateNotificationAccountInfo(ctx context.Context, req *pbu func (s *userServer) SearchNotificationAccount(ctx context.Context, req *pbuser.SearchNotificationAccountReq) (*pbuser.SearchNotificationAccountResp, error) { // Check if user is an admin - if err := authverify.CheckIMAdmin(ctx); err != nil { + if err := authverify.CheckIMAdmin(ctx, s.config); err != nil { return nil, err } @@ -671,7 +681,7 @@ func (s *userServer) userModelToResp(users []*relation.UserModel, pagination pag accounts := make([]*pbuser.NotificationAccountInfo, 0) var total int64 for _, v := range users { - if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, config.Config.IMAdmin.UserID) { + if v.AppMangerLevel == constant.AppNotificationAdmin && !utils.IsContain(v.UserID, s.config.IMAdmin.UserID) { temp := &pbuser.NotificationAccountInfo{ UserID: v.UserID, FaceURL: v.FaceURL, diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index 9b74a5767..ce87e9b90 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -23,37 +23,38 @@ import ( "time" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/redis/go-redis/v9" "github.com/robfig/cron/v3" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) -func StartTask() error { - fmt.Println("Cron task start, config:", config.Config.ChatRecordsClearTime) +func StartTask(config *config.GlobalConfig) error { + fmt.Println("cron task start, config", config.ChatRecordsClearTime) - msgTool, err := InitMsgTool() + msgTool, err := InitMsgTool(config) if err != nil { return err } msgTool.convertTools() - rdb, err := cache.NewRedis() + rdb, err := cache.NewRedis(config) if err != nil { return err } // register cron tasks var crontab = cron.New() - fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.Config.ChatRecordsClearTime) - _, err = crontab.AddFunc(config.Config.ChatRecordsClearTime, cronWrapFunc(rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) + fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.ChatRecordsClearTime) + _, err = crontab.AddFunc(config.ChatRecordsClearTime, cronWrapFunc(config,rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) if err != nil { return errs.Wrap(err) } - fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.Config.MsgDestructTime) - _, err = crontab.AddFunc(config.Config.MsgDestructTime, cronWrapFunc(rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) + fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.MsgDestructTime) + _, err = crontab.AddFunc(config.MsgDestructTime, cronWrapFunc(config,rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) if err != nil { return errs.Wrap(err, "cron_conversations_destruct_msgs") } @@ -91,8 +92,8 @@ func netlock(rdb redis.UniversalClient, key string, ttl time.Duration) bool { return ok } -func cronWrapFunc(rdb redis.UniversalClient, key string, fn func()) func() { - enableCronLocker := config.Config.EnableCronLocker +func cronWrapFunc(config *config.GlobalConfig, rdb redis.UniversalClient, key string, fn func()) func() { + enableCronLocker := config.EnableCronLocker return func() { // if don't enable cron-locker, call fn directly. if !enableCronLocker { diff --git a/internal/tools/cron_task_test.go b/internal/tools/cron_task_test.go index 28bc2c945..fcae5a5f6 100644 --- a/internal/tools/cron_task_test.go +++ b/internal/tools/cron_task_test.go @@ -15,8 +15,12 @@ package tools import ( + "flag" "fmt" + "github.com/OpenIMSDK/tools/errs" + "gopkg.in/yaml.v3" "math/rand" + "os" "sync" "testing" "time" @@ -61,7 +65,7 @@ func TestCronWrapFunc(t *testing.T) { start := time.Now() key := fmt.Sprintf("cron-%v", rand.Int31()) crontab := cron.New(cron.WithSeconds()) - crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, cb)) + crontab.AddFunc("*/1 * * * * *", cronWrapFunc(config.NewGlobalConfig(), rdb, key, cb)) crontab.Start() <-done @@ -71,7 +75,11 @@ func TestCronWrapFunc(t *testing.T) { } func TestCronWrapFuncWithNetlock(t *testing.T) { - config.Config.EnableCronLocker = true + conf, err := initCfg() + if err != nil { + panic(err) + } + conf.EnableCronLocker = true rdb := redis.NewClient(&redis.Options{}) defer rdb.Close() @@ -80,10 +88,10 @@ func TestCronWrapFuncWithNetlock(t *testing.T) { crontab := cron.New(cron.WithSeconds()) key := fmt.Sprintf("cron-%v", rand.Int31()) - crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, func() { + crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() { done <- "host1" })) - crontab.AddFunc("*/1 * * * * *", cronWrapFunc(rdb, key, func() { + crontab.AddFunc("*/1 * * * * *", cronWrapFunc(conf, rdb, key, func() { done <- "host2" })) crontab.Start() @@ -94,3 +102,22 @@ func TestCronWrapFuncWithNetlock(t *testing.T) { crontab.Stop() } + +func initCfg() (*config.GlobalConfig, error) { + const ( + defaultCfgPath = "../../../../../config/config.yaml" + ) + + cfgPath := flag.String("c", defaultCfgPath, "Path to the configuration file") + data, err := os.ReadFile(*cfgPath) + if err != nil { + return nil, errs.Wrap(err, "ReadFile unmarshal failed") + } + + conf := config.NewGlobalConfig() + err = yaml.Unmarshal(data, &conf) + if err != nil { + return nil, errs.Wrap(err, "InitConfig unmarshal failed") + } + return conf, nil +} diff --git a/internal/tools/msg.go b/internal/tools/msg.go index f2df0d337..67c3895cb 100644 --- a/internal/tools/msg.go +++ b/internal/tools/msg.go @@ -46,10 +46,12 @@ type MsgTool struct { userDatabase controller.UserDatabase groupDatabase controller.GroupDatabase msgNotificationSender *notification.MsgNotificationSender + Config *config.GlobalConfig } func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controller.UserDatabase, - groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase, msgNotificationSender *notification.MsgNotificationSender, + groupDatabase controller.GroupDatabase, conversationDatabase controller.ConversationDatabase, + msgNotificationSender *notification.MsgNotificationSender, config *config.GlobalConfig, ) *MsgTool { return &MsgTool{ msgDatabase: msgDatabase, @@ -57,32 +59,33 @@ func NewMsgTool(msgDatabase controller.CommonMsgDatabase, userDatabase controlle groupDatabase: groupDatabase, conversationDatabase: conversationDatabase, msgNotificationSender: msgNotificationSender, + Config: config, } } -func InitMsgTool() (*MsgTool, error) { - rdb, err := cache.NewRedis() +func InitMsgTool(config *config.GlobalConfig) (*MsgTool, error) { + rdb, err := cache.NewRedis(config) if err != nil { return nil, err } - mongo, err := unrelation.NewMongo() + mongo, err := unrelation.NewMongo(config) if err != nil { return nil, err } - discov, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) + discov, err := kdisc.NewDiscoveryRegister(config) if err != nil { return nil, err } discov.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) - userDB, err := mgo.NewUserMongo(mongo.GetDatabase()) + userDB, err := mgo.NewUserMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return nil, err } - msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase()) + msgDatabase, err := controller.InitCommonMsgDatabase(rdb, mongo.GetDatabase(config.Mongo.Database), config) if err != nil { return nil, err } - userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase()) + userMongoDB := unrelation.NewUserMongoDriver(mongo.GetDatabase(config.Mongo.Database)) ctxTx := tx.NewMongo(mongo.GetClient()) userDatabase := controller.NewUserDatabase( userDB, @@ -90,19 +93,19 @@ func InitMsgTool() (*MsgTool, error) { ctxTx, userMongoDB, ) - groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase()) + groupDB, err := mgo.NewGroupMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return nil, err } - groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase()) + groupMemberDB, err := mgo.NewGroupMember(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return nil, err } - groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase()) + groupRequestDB, err := mgo.NewGroupRequestMgo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return nil, err } - conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase()) + conversationDB, err := mgo.NewConversationMongo(mongo.GetDatabase(config.Mongo.Database)) if err != nil { return nil, err } @@ -112,9 +115,9 @@ func InitMsgTool() (*MsgTool, error) { cache.NewConversationRedis(rdb, cache.GetDefaultOpt(), conversationDB), ctxTx, ) - msgRpcClient := rpcclient.NewMessageRpcClient(discov) - msgNotificationSender := notification.NewMsgNotificationSender(rpcclient.WithRpcClient(&msgRpcClient)) - msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender) + msgRpcClient := rpcclient.NewMessageRpcClient(discov, config) + msgNotificationSender := notification.NewMsgNotificationSender(config, rpcclient.WithRpcClient(&msgRpcClient)) + msgTool := NewMsgTool(msgDatabase, userDatabase, groupDatabase, conversationDatabase, msgNotificationSender, config) return msgTool, nil } @@ -176,8 +179,8 @@ func (c *MsgTool) AllConversationClearMsgAndFixSeq() { func (c *MsgTool) ClearConversationsMsg(ctx context.Context, conversationIDs []string) { for _, conversationID := range conversationIDs { - if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(config.Config.RetainChatRecords*24*60*60)); err != nil { - log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", config.Config.RetainChatRecords) + if err := c.msgDatabase.DeleteConversationMsgsAndSetMinSeq(ctx, conversationID, int64(c.Config.RetainChatRecords*24*60*60)); err != nil { + log.ZError(ctx, "DeleteUserSuperGroupMsgsAndSetMinSeq failed", err, "conversationID", conversationID, "DBRetainChatRecords", c.Config.RetainChatRecords) } if err := c.checkMaxSeq(ctx, conversationID); err != nil { log.ZError(ctx, "fixSeq failed", err, "conversationID", conversationID) diff --git a/pkg/authverify/token.go b/pkg/authverify/token.go index 0a46af3ec..26c43532d 100644 --- a/pkg/authverify/token.go +++ b/pkg/authverify/token.go @@ -26,61 +26,60 @@ import ( "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) -func Secret() jwt.Keyfunc { +func Secret(secret string) jwt.Keyfunc { return func(token *jwt.Token) (any, error) { - return []byte(config.Config.Secret), nil + return []byte(secret), nil } } -func CheckAccessV3(ctx context.Context, ownerUserID string) (err error) { +func CheckAccessV3(ctx context.Context, ownerUserID string, config *config.GlobalConfig) (err error) { opUserID := mcontext.GetOpUserID(ctx) - if len(config.Config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Config.Manager.UserID) { + if len(config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Manager.UserID) { return nil } - if utils.IsContain(opUserID, config.Config.IMAdmin.UserID) { + if utils.IsContain(opUserID, config.IMAdmin.UserID) { return nil } if opUserID == ownerUserID { return nil } - return errs.Wrap(errs.ErrNoPermission, "CheckAccessV3: no permission for user "+opUserID) + return errs.ErrNoPermission.Wrap("ownerUserID", ownerUserID) } -func IsAppManagerUid(ctx context.Context) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID)) || - utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) +func IsAppManagerUid(ctx context.Context, config *config.GlobalConfig) bool { + return (len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID)) || + utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) } -func CheckAdmin(ctx context.Context) error { - if len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { +func CheckAdmin(ctx context.Context, config *config.GlobalConfig) error { + if len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID) { return nil } - if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) { + if utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) { return nil } return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not admin userID", mcontext.GetOpUserID(ctx))) } - -func CheckIMAdmin(ctx context.Context) error { - if utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.IMAdmin.UserID) { +func CheckIMAdmin(ctx context.Context, config *config.GlobalConfig) error { + if utils.IsContain(mcontext.GetOpUserID(ctx), config.IMAdmin.UserID) { return nil } - if len(config.Config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Config.Manager.UserID) { + if len(config.Manager.UserID) > 0 && utils.IsContain(mcontext.GetOpUserID(ctx), config.Manager.UserID) { return nil } return errs.ErrNoPermission.Wrap(fmt.Sprintf("user %s is not CheckIMAdmin userID", mcontext.GetOpUserID(ctx))) } -func ParseRedisInterfaceToken(redisToken any) (*tokenverify.Claims, error) { - return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret()) +func ParseRedisInterfaceToken(redisToken any, secret string) (*tokenverify.Claims, error) { + return tokenverify.GetClaimFromToken(string(redisToken.([]uint8)), Secret(secret)) } -func IsManagerUserID(opUserID string) bool { - return (len(config.Config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Config.Manager.UserID)) || utils.IsContain(opUserID, config.Config.IMAdmin.UserID) +func IsManagerUserID(opUserID string, config *config.GlobalConfig) bool { + return (len(config.Manager.UserID) > 0 && utils.IsContain(opUserID, config.Manager.UserID)) || utils.IsContain(opUserID, config.IMAdmin.UserID) } -func WsVerifyToken(token, userID string, platformID int) error { - claim, err := tokenverify.GetClaimFromToken(token, Secret()) +func WsVerifyToken(token, userID, secret string, platformID int) error { + claim, err := tokenverify.GetClaimFromToken(token, Secret(secret)) if err != nil { return err } diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index 47e4116e3..859508ce3 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -15,54 +15,44 @@ package cmd import ( - "errors" - "fmt" - "github.com/OpenIMSDK/protocol/constant" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/internal/api" "github.com/spf13/cobra" + + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type ApiCmd struct { *RootCmd + initFunc func(config *config.GlobalConfig, port int, promPort int) error } func NewApiCmd() *ApiCmd { - ret := &ApiCmd{NewRootCmd("api")} + ret := &ApiCmd{RootCmd: NewRootCmd("api"), initFunc: api.Start} ret.SetRootCmdPt(ret) - + ret.addPreRun() + ret.addRunE() return ret } -// AddApi configures the API command to run with specified ports for the API and Prometheus monitoring. -// It ensures error handling for port retrieval and only proceeds if both port numbers are successfully obtained. -func (a *ApiCmd) AddApi(f func(port int, promPort int) error) { - a.Command.RunE = func(cmd *cobra.Command, args []string) error { - port, err := a.getPortFlag(cmd) - if err != nil { - return err - } - - promPort, err := a.getPrometheusPortFlag(cmd) - if err != nil { - return err - } +func (a *ApiCmd) addPreRun() { + a.Command.PreRun = func(cmd *cobra.Command, args []string) { + a.port = a.getPortFlag(cmd) + a.prometheusPort = a.getPrometheusPortFlag(cmd) + } +} - return f(port, promPort) +func (a *ApiCmd) addRunE() { + a.Command.RunE = func(cmd *cobra.Command, args []string) error { + return a.initFunc(a.config, a.port, a.prometheusPort) } } -func (a *ApiCmd) GetPortFromConfig(portType string) (int, error) { +func (a *ApiCmd) GetPortFromConfig(portType string) int { if portType == constant.FlagPort { - if len(config2.Config.Api.OpenImApiPort) > 0 { - return config2.Config.Api.OpenImApiPort[0], nil - } - return 0, errors.New("API port configuration is empty or missing") + return a.config.Api.OpenImApiPort[0] } else if portType == constant.FlagPrometheusPort { - if len(config2.Config.Prometheus.ApiPrometheusPort) > 0 { - return config2.Config.Prometheus.ApiPrometheusPort[0], nil - } - return 0, errors.New("Prometheus port configuration is empty or missing") + return a.config.Prometheus.ApiPrometheusPort[0] } - return 0, fmt.Errorf("unknown port type: %s", portType) + return 0 } diff --git a/pkg/common/cmd/cron_task.go b/pkg/common/cmd/cron_task.go index fa7a46351..d8c9dd2a8 100644 --- a/pkg/common/cmd/cron_task.go +++ b/pkg/common/cmd/cron_task.go @@ -14,29 +14,35 @@ package cmd -import "github.com/spf13/cobra" +import ( + "github.com/openimsdk/open-im-server/v3/internal/tools" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" +) type CronTaskCmd struct { *RootCmd + initFunc func(config *config.GlobalConfig) error } func NewCronTaskCmd() *CronTaskCmd { - ret := &CronTaskCmd{NewRootCmd("cronTask", WithCronTaskLogName())} + ret := &CronTaskCmd{RootCmd: NewRootCmd("cronTask", WithCronTaskLogName()), + initFunc: tools.StartTask} + ret.addRunE() ret.SetRootCmdPt(ret) return ret } -func (c *CronTaskCmd) addRunE(f func() error) { +func (c *CronTaskCmd) addRunE() { c.Command.RunE = func(cmd *cobra.Command, args []string) error { - return f() + return c.initFunc(c.config) } } -func (c *CronTaskCmd) Exec(f func() error) error { - c.addRunE(f) +func (c *CronTaskCmd) Exec() error { return c.Execute() } -func (c *CronTaskCmd) GetPortFromConfig(portType string) (int, error) { - return 0, nil +func (c *CronTaskCmd) GetPortFromConfig(portType string) int { + return 0 } diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 403d0ec84..3a939fa97 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -15,13 +15,13 @@ package cmd import ( - "errors" + "log" + + "github.com/spf13/cobra" "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/internal/msggateway" - v3config "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/spf13/cobra" ) type MsgGatewayCmd struct { @@ -30,6 +30,7 @@ type MsgGatewayCmd struct { func NewMsgGatewayCmd() *MsgGatewayCmd { ret := &MsgGatewayCmd{NewRootCmd("msgGateway")} + ret.addRunE() ret.SetRootCmdPt(ret) return ret } @@ -38,67 +39,39 @@ func (m *MsgGatewayCmd) AddWsPortFlag() { m.Command.Flags().IntP(constant.FlagWsPort, "w", 0, "ws server listen port") } -func (m *MsgGatewayCmd) getWsPortFlag(cmd *cobra.Command) (int, error) { +func (m *MsgGatewayCmd) getWsPortFlag(cmd *cobra.Command) int { port, err := cmd.Flags().GetInt(constant.FlagWsPort) if err != nil { - return 0, errs.Wrap(err, "error getting ws port flag") + log.Println("Error getting ws port flag:", err) } if port == 0 { - port, _ = m.PortFromConfig(constant.FlagWsPort) + port = m.PortFromConfig(constant.FlagWsPort) } - return port, nil + return port } func (m *MsgGatewayCmd) addRunE() { m.Command.RunE = func(cmd *cobra.Command, args []string) error { - wsPort, err := m.getWsPortFlag(cmd) - if err != nil { - return errs.Wrap(err, "failed to get WS port flag") - } - port, err := m.getPortFlag(cmd) - if err != nil { - return err - } - prometheusPort, err := m.getPrometheusPortFlag(cmd) - if err != nil { - return err - } - return msggateway.RunWsAndServer(port, wsPort, prometheusPort) + return msggateway.RunWsAndServer(m.config, m.getPortFlag(cmd), m.getWsPortFlag(cmd), m.getPrometheusPortFlag(cmd)) } } func (m *MsgGatewayCmd) Exec() error { - m.addRunE() return m.Execute() } -func (m *MsgGatewayCmd) GetPortFromConfig(portType string) (int, error) { - var port int - var exists bool - +func (m *MsgGatewayCmd) GetPortFromConfig(portType string) int { switch portType { case constant.FlagWsPort: - if len(v3config.Config.LongConnSvr.OpenImWsPort) > 0 { - port = v3config.Config.LongConnSvr.OpenImWsPort[0] - exists = true - } + return m.config.LongConnSvr.OpenImWsPort[0] case constant.FlagPort: - if len(v3config.Config.LongConnSvr.OpenImMessageGatewayPort) > 0 { - port = v3config.Config.LongConnSvr.OpenImMessageGatewayPort[0] - exists = true - } + return m.config.LongConnSvr.OpenImMessageGatewayPort[0] case constant.FlagPrometheusPort: - if len(v3config.Config.Prometheus.MessageGatewayPrometheusPort) > 0 { - port = v3config.Config.Prometheus.MessageGatewayPrometheusPort[0] - exists = true - } - } + return m.config.Prometheus.MessageGatewayPrometheusPort[0] - if !exists { - return 0, errs.Wrap(errors.New("port type '%s' not found in configuration"), portType) + default: + return 0 } - - return port, nil } diff --git a/pkg/common/cmd/msg_gateway_test.go b/pkg/common/cmd/msg_gateway_test.go index 106ad74ec..c0ea2b057 100644 --- a/pkg/common/cmd/msg_gateway_test.go +++ b/pkg/common/cmd/msg_gateway_test.go @@ -44,7 +44,7 @@ func TestMsgGatewayCmd_GetPortFromConfig(t *testing.T) { } for _, tt := range tests { t.Run(tt.portType, func(t *testing.T) { - got, _ := msgGatewayCmd.GetPortFromConfig(tt.portType) + got := msgGatewayCmd.GetPortFromConfig(tt.portType) assert.Equal(t, tt.want, got) }) } diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index 4db24fac5..e46b66b52 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -16,11 +16,10 @@ package cmd import ( "fmt" - "github.com/OpenIMSDK/protocol/constant" - "github.com/openimsdk/open-im-server/v3/internal/msgtransfer" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/spf13/cobra" + + "github.com/openimsdk/open-im-server/v3/internal/msgtransfer" ) type MsgTransferCmd struct { @@ -29,37 +28,29 @@ type MsgTransferCmd struct { func NewMsgTransferCmd() *MsgTransferCmd { ret := &MsgTransferCmd{NewRootCmd("msgTransfer")} + ret.addRunE() ret.SetRootCmdPt(ret) return ret } func (m *MsgTransferCmd) addRunE() { m.Command.RunE = func(cmd *cobra.Command, args []string) error { - prometheusPort, err := m.getPrometheusPortFlag(cmd) - if err != nil { - return err - } - return msgtransfer.StartTransfer(prometheusPort) + return msgtransfer.StartTransfer(m.config, m.getPrometheusPortFlag(cmd)) } } func (m *MsgTransferCmd) Exec() error { - m.addRunE() return m.Execute() } -func (m *MsgTransferCmd) GetPortFromConfig(portType string) (int, error) { +func (m *MsgTransferCmd) GetPortFromConfig(portType string) int { if portType == constant.FlagPort { - return 0, nil + return 0 } else if portType == constant.FlagPrometheusPort { n := m.getTransferProgressFlagValue() - - if n < len(config2.Config.Prometheus.MessageTransferPrometheusPort) { - return config2.Config.Prometheus.MessageTransferPrometheusPort[n], nil - } - return 0, fmt.Errorf("index out of range for MessageTransferPrometheusPort with index %d", n) + return m.config.Prometheus.MessageTransferPrometheusPort[n] } - return 0, fmt.Errorf("unknown port type: %s", portType) + return 0 } func (m *MsgTransferCmd) AddTransferProgressFlag() { @@ -67,10 +58,10 @@ func (m *MsgTransferCmd) AddTransferProgressFlag() { } func (m *MsgTransferCmd) getTransferProgressFlagValue() int { - nindex, err := m.Command.Flags().GetInt(constant.FlagTransferProgressIndex) + nIndex, err := m.Command.Flags().GetInt(constant.FlagTransferProgressIndex) if err != nil { - fmt.Println("get transfercmd error,make sure it is k8s env or not") + fmt.Println("get transfer cmd error,make sure it is k8s env or not") return 0 } - return nindex + return nIndex } diff --git a/pkg/common/cmd/msg_utils.go b/pkg/common/cmd/msg_utils.go index 03c1cab67..df15acd87 100644 --- a/pkg/common/cmd/msg_utils.go +++ b/pkg/common/cmd/msg_utils.go @@ -22,6 +22,7 @@ import ( type MsgUtilsCmd struct { cobra.Command + MsgTool *tools.MsgTool } func (m *MsgUtilsCmd) AddUserIDFlag() { @@ -135,7 +136,7 @@ func NewSeqCmd() *SeqCmd { func (s *SeqCmd) GetSeqCmd() *cobra.Command { s.Command.Run = func(cmdLines *cobra.Command, args []string) { - _, err := tools.InitMsgTool() + _, err := tools.InitMsgTool(s.MsgTool.Config) if err != nil { util.ExitWithError(err) } diff --git a/pkg/common/cmd/root.go b/pkg/common/cmd/root.go index 591bfc804..478942a5b 100644 --- a/pkg/common/cmd/root.go +++ b/pkg/common/cmd/root.go @@ -26,7 +26,7 @@ import ( ) type RootCmdPt interface { - GetPortFromConfig(portType string) (int, error) + GetPortFromConfig(portType string) int } type RootCmd struct { @@ -35,6 +35,11 @@ type RootCmd struct { port int prometheusPort int cmdItf RootCmdPt + config *config.GlobalConfig +} + +func (rc *RootCmd) Port() int { + return rc.port } type CmdOpts struct { @@ -54,7 +59,7 @@ func WithLogName(logName string) func(*CmdOpts) { } func NewRootCmd(name string, opts ...func(*CmdOpts)) *RootCmd { - rootCmd := &RootCmd{Name: name} + rootCmd := &RootCmd{Name: name, config: config.NewGlobalConfig()} cmd := cobra.Command{ Use: "Start openIM application", Short: fmt.Sprintf(`Start %s `, name), @@ -96,7 +101,7 @@ func (rc *RootCmd) applyOptions(opts ...func(*CmdOpts)) *CmdOpts { } func (rc *RootCmd) initializeLogger(cmdOpts *CmdOpts) error { - logConfig := config.Config.Log + logConfig := rc.config.Log return log.InitFromConfig( @@ -129,41 +134,36 @@ func (r *RootCmd) AddPortFlag() { r.Command.Flags().IntP(constant.FlagPort, "p", 0, "server listen port") } -func (r *RootCmd) getPortFlag(cmd *cobra.Command) (int, error) { +func (r *RootCmd) getPortFlag(cmd *cobra.Command) int { port, err := cmd.Flags().GetInt(constant.FlagPort) if err != nil { // Wrapping the error with additional context - return 0, errs.Wrap(err, "error getting port flag") + return 0 } if port == 0 { - port, _ = r.PortFromConfig(constant.FlagPort) - // port, err := r.PortFromConfig(constant.FlagPort) - // if err != nil { - // // Optionally wrap the error if it's an internal error needing context - // return 0, errs.Wrap(err, "error getting port from config") - // } + port = r.PortFromConfig(constant.FlagPort) } - return port, nil + return port } // // GetPortFlag returns the port flag. -func (r *RootCmd) GetPortFlag() (int, error) { - return r.port, nil +func (r *RootCmd) GetPortFlag() int { + return r.port } func (r *RootCmd) AddPrometheusPortFlag() { r.Command.Flags().IntP(constant.FlagPrometheusPort, "", 0, "server prometheus listen port") } -func (r *RootCmd) getPrometheusPortFlag(cmd *cobra.Command) (int, error) { +func (r *RootCmd) getPrometheusPortFlag(cmd *cobra.Command) int { port, err := cmd.Flags().GetInt(constant.FlagPrometheusPort) if err != nil || port == 0 { - port, err = r.PortFromConfig(constant.FlagPrometheusPort) + port = r.PortFromConfig(constant.FlagPrometheusPort) if err != nil { - return 0, err + return 0 } } - return port, nil + return port } func (r *RootCmd) GetPrometheusPortFlag() int { @@ -173,7 +173,7 @@ func (r *RootCmd) GetPrometheusPortFlag() int { func (r *RootCmd) getConfFromCmdAndInit(cmdLines *cobra.Command) error { configFolderPath, _ := cmdLines.Flags().GetString(constant.FlagConf) fmt.Println("The directory of the configuration file to start the process:", configFolderPath) - return config2.InitConfig(configFolderPath) + return config2.InitConfig(r.config, configFolderPath) } func (r *RootCmd) Execute() error { @@ -184,11 +184,8 @@ func (r *RootCmd) AddCommand(cmds ...*cobra.Command) { r.Command.AddCommand(cmds...) } -func (r *RootCmd) PortFromConfig(portType string) (int, error) { +func (r *RootCmd) PortFromConfig(portType string) int { // Retrieve the port and cache it - port, err := r.cmdItf.GetPortFromConfig(portType) - if err != nil { - return 0, err - } - return port, nil + port := r.cmdItf.GetPortFromConfig(portType) + return port } diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index e30de93a9..5199524e7 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -16,100 +16,144 @@ package cmd import ( "errors" - "fmt" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/errs" - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" "github.com/spf13/cobra" "google.golang.org/grpc" + + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + + "github.com/OpenIMSDK/tools/discoveryregistry" + + "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" ) +type rpcInitFuc func(config *config2.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error + type RpcCmd struct { *RootCmd + RpcRegisterName string + initFunc rpcInitFuc } -func NewRpcCmd(name string) *RpcCmd { - ret := &RpcCmd{NewRootCmd(name)} +func NewRpcCmd(name string, initFunc rpcInitFuc) *RpcCmd { + ret := &RpcCmd{RootCmd: NewRootCmd(name), initFunc: initFunc} + ret.addPreRun() + ret.addRunE() ret.SetRootCmdPt(ret) return ret } -func (a *RpcCmd) Exec() error { - a.Command.RunE = func(cmd *cobra.Command, args []string) error { - portFlag, err := a.getPortFlag(cmd) - if err != nil { - return err - } - a.port = portFlag +func (a *RpcCmd) addPreRun() { + a.Command.PreRun = func(cmd *cobra.Command, args []string) { + a.port = a.getPortFlag(cmd) + a.prometheusPort = a.getPrometheusPortFlag(cmd) + } +} - prometheusPort, err := a.getPrometheusPortFlag(cmd) +func (a *RpcCmd) addRunE() { + a.Command.RunE = func(cmd *cobra.Command, args []string) error { + rpcRegisterName, err := a.GetRpcRegisterNameFromConfig() if err != nil { return err + } else { + return a.StartSvr(rpcRegisterName, a.initFunc) } - a.prometheusPort = prometheusPort - - return nil } - return a.Execute() } -func (a *RpcCmd) StartSvr(name string, rpcFn func(discov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error { - portFlag, err := a.GetPortFlag() - if err != nil { - return err - } else { - a.port = portFlag - } - - return startrpc.Start(portFlag, name, a.GetPrometheusPortFlag(), rpcFn) +func (a *RpcCmd) Exec() error { + return a.Execute() } -func (a *RpcCmd) GetPortFromConfig(portType string) (int, error) { - portConfigMap := map[string]map[string]int{ - RpcPushServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImPushPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.PushPrometheusPort[0], - }, - RpcAuthServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImAuthPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.AuthPrometheusPort[0], - }, - RpcConversationServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImConversationPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.ConversationPrometheusPort[0], - }, - RpcFriendServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImFriendPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.FriendPrometheusPort[0], - }, - RpcGroupServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImGroupPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.GroupPrometheusPort[0], - }, - RpcMsgServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImMessagePort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.MessagePrometheusPort[0], - }, - RpcThirdServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImThirdPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.ThirdPrometheusPort[0], - }, - RpcUserServer: { - constant.FlagPort: config2.Config.RpcPort.OpenImUserPort[0], - constant.FlagPrometheusPort: config2.Config.Prometheus.UserPrometheusPort[0], - }, +func (a *RpcCmd) StartSvr(name string, rpcFn func(config *config2.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error) error { + if a.GetPortFlag() == 0 { + return errs.Wrap(errors.New("port is required")) } + return startrpc.Start(a.GetPortFlag(), name, a.GetPrometheusPortFlag(), a.config, rpcFn) +} - if portMap, ok := portConfigMap[a.Name]; ok { - if port, ok := portMap[portType]; ok { - return port, nil - } else { - return 0, errs.Wrap(errors.New("port type not found"), fmt.Sprintf("Failed to get port for %s", a.Name)) +func (a *RpcCmd) GetPortFromConfig(portType string) int { + switch a.Name { + case RpcPushServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImPushPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.PushPrometheusPort[0] + } + case RpcAuthServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImAuthPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.AuthPrometheusPort[0] + } + case RpcConversationServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImConversationPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.ConversationPrometheusPort[0] + } + case RpcFriendServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImFriendPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.FriendPrometheusPort[0] + } + case RpcGroupServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImGroupPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.GroupPrometheusPort[0] + } + case RpcMsgServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImMessagePort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.MessagePrometheusPort[0] + } + case RpcThirdServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImThirdPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.ThirdPrometheusPort[0] + } + case RpcUserServer: + if portType == constant.FlagPort { + return a.config.RpcPort.OpenImUserPort[0] + } + if portType == constant.FlagPrometheusPort { + return a.config.Prometheus.UserPrometheusPort[0] } } + return 0 +} - return 0, errs.Wrap(fmt.Errorf("server name '%s' not found", a.Name), "Failed to get port configuration") +func (a *RpcCmd) GetRpcRegisterNameFromConfig() (string, error) { + switch a.Name { + case RpcPushServer: + return a.config.RpcRegisterName.OpenImPushName, nil + case RpcAuthServer: + return a.config.RpcRegisterName.OpenImAuthName, nil + case RpcConversationServer: + return a.config.RpcRegisterName.OpenImConversationName, nil + case RpcFriendServer: + return a.config.RpcRegisterName.OpenImFriendName, nil + case RpcGroupServer: + return a.config.RpcRegisterName.OpenImGroupName, nil + case RpcMsgServer: + return a.config.RpcRegisterName.OpenImMsgName, nil + case RpcThirdServer: + return a.config.RpcRegisterName.OpenImThirdName, nil + case RpcUserServer: + return a.config.RpcRegisterName.OpenImUserName, nil + } + return "", errs.Wrap(errors.New("can not get rpc register name"), a.Name) } diff --git a/pkg/common/config/config.go b/pkg/common/config/config.go index 7ee55d876..ac42395bf 100644 --- a/pkg/common/config/config.go +++ b/pkg/common/config/config.go @@ -21,7 +21,7 @@ import ( "gopkg.in/yaml.v3" ) -var Config configStruct +var Config GlobalConfig const ConfKey = "conf" @@ -57,7 +57,7 @@ type MYSQL struct { SlowThreshold int `yaml:"slowThreshold"` } -type configStruct struct { +type GlobalConfig struct { Envs struct { Discovery string `yaml:"discovery"` } @@ -339,6 +339,10 @@ type configStruct struct { Notification notification `yaml:"notification"` } +func NewGlobalConfig() *GlobalConfig { + return &GlobalConfig{} +} + type notification struct { GroupCreated NotificationConf `yaml:"groupCreated"` GroupInfoSet NotificationConf `yaml:"groupInfoSet"` @@ -378,7 +382,7 @@ type notification struct { ConversationSetPrivate NotificationConf `yaml:"conversationSetPrivate"` } -func (c *configStruct) GetServiceNames() []string { +func (c *GlobalConfig) GetServiceNames() []string { return []string{ c.RpcRegisterName.OpenImUserName, c.RpcRegisterName.OpenImFriendName, @@ -392,7 +396,7 @@ func (c *configStruct) GetServiceNames() []string { } } -func (c *configStruct) RegisterConf2Registry(registry discoveryregistry.SvcDiscoveryRegistry) error { +func (c *GlobalConfig) RegisterConf2Registry(registry discoveryregistry.SvcDiscoveryRegistry) error { data, err := yaml.Marshal(c) if err != nil { return err @@ -400,11 +404,11 @@ func (c *configStruct) RegisterConf2Registry(registry discoveryregistry.SvcDisco return registry.RegisterConf2Registry(ConfKey, data) } -func (c *configStruct) GetConfFromRegistry(registry discoveryregistry.SvcDiscoveryRegistry) ([]byte, error) { +func (c *GlobalConfig) GetConfFromRegistry(registry discoveryregistry.SvcDiscoveryRegistry) ([]byte, error) { return registry.GetConfFromRegistry(ConfKey) } -func (c *configStruct) EncodeConfig() []byte { +func (c *GlobalConfig) EncodeConfig() []byte { buf := bytes.NewBuffer(nil) if err := yaml.NewEncoder(buf).Encode(c); err != nil { panic(err) diff --git a/pkg/common/config/parse.go b/pkg/common/config/parse.go index a73665386..bfbf6daf7 100644 --- a/pkg/common/config/parse.go +++ b/pkg/common/config/parse.go @@ -21,10 +21,10 @@ import ( "path/filepath" "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/tools/errs" + "gopkg.in/yaml.v3" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - "gopkg.in/yaml.v3" ) //go:embed version @@ -36,38 +36,32 @@ const ( DefaultFolderPath = "../config/" ) -// GetDefaultConfigPath returns the absolute path to the default configuration directory -// relative to the executable's location. It is intended for use in Kubernetes container configurations. -// Errors are returned to the caller to allow for flexible error handling. -func GetDefaultConfigPath() (string, error) { +// return absolude path join ../config/, this is k8s container config path. +func GetDefaultConfigPath() string { executablePath, err := os.Executable() if err != nil { - return "", errs.Wrap(err, "failed to get executable path") + fmt.Println("GetDefaultConfigPath error:", err.Error()) + return "" } - // Calculate the config path as a directory relative to the executable's location configPath, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../config/")) if err != nil { - return "", errs.Wrap(err, "failed to get output directory") + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) + os.Exit(1) } - return configPath, nil + return configPath } -// GetProjectRoot returns the absolute path of the project root directory by navigating up from the directory -// containing the executable. It provides a detailed error if the path cannot be determined. -func GetProjectRoot() (string, error) { - executablePath, err := os.Executable() - if err != nil { - return "", errs.Wrap(err, "failed to retrieve executable path") - } +// getProjectRoot returns the absolute path of the project root directory. +func GetProjectRoot() string { + executablePath, _ := os.Executable() - // Attempt to compute the project root by navigating up from the executable's directory projectRoot, err := genutil.OutDir(filepath.Join(filepath.Dir(executablePath), "../../../../..")) if err != nil { - return "", err + fmt.Fprintf(os.Stderr, "failed to get output directory: %v\n", err) + os.Exit(1) } - - return projectRoot, nil + return projectRoot } func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options { @@ -93,62 +87,41 @@ func GetOptionsByNotification(cfg NotificationConf) msgprocessor.Options { // If the specified config file does not exist, it attempts to load from the project's default "config" directory. // It logs informative messages regarding the configuration path being used. func initConfig(config any, configName, configFolderPath string) error { - configFilePath := filepath.Join(configFolderPath, configName) - _, err := os.Stat(configFilePath) + configFolderPath = filepath.Join(configFolderPath, configName) + _, err := os.Stat(configFolderPath) if err != nil { if !os.IsNotExist(err) { - return errs.Wrap(err, fmt.Sprintf("failed to check existence of config file at path: %s", configFilePath)) + fmt.Println("stat config path error:", err.Error()) + return fmt.Errorf("stat config path error: %w", err) } - var projectRoot string - projectRoot, err = GetProjectRoot() - if err != nil { - return err - } - configFilePath = filepath.Join(projectRoot, "config", configName) - fmt.Printf("Configuration file not found at specified path. Falling back to project path: %s\n", configFilePath) + configFolderPath = filepath.Join(GetProjectRoot(), "config", configName) + fmt.Println("flag's path,enviment's path,default path all is not exist,using project path:", configFolderPath) } - - data, err := os.ReadFile(configFilePath) + data, err := os.ReadFile(configFolderPath) if err != nil { - // Wrap and return the error if reading the configuration file fails. - return errs.Wrap(err, fmt.Sprintf("failed to read configuration file at path: %s", configFilePath)) + return fmt.Errorf("read file error: %w", err) } - if err = yaml.Unmarshal(data, config); err != nil { - // Wrap and return the error if unmarshalling the YAML configuration fails. - return errs.Wrap(err, "failed to unmarshal YAML configuration") + return fmt.Errorf("unmarshal yaml error: %w", err) } + fmt.Println("The path of the configuration file to start the process:", configFolderPath) - fmt.Printf("Configuration file loaded successfully from path: %s\n", configFilePath) return nil } -// InitConfig initializes the application configuration by loading it from a specified folder path. -// If the folder path is not provided, it attempts to use the OPENIMCONFIG environment variable, -// and as a fallback, it uses the default configuration path. It loads both the main configuration -// and notification configuration, wrapping errors for better context. -func InitConfig(configFolderPath string) error { - // Use the provided config folder path, or fallback to environment variable or default path +func InitConfig(config *GlobalConfig, configFolderPath string) error { if configFolderPath == "" { - configFolderPath = os.Getenv("OPENIMCONFIG") - if configFolderPath == "" { - var err error - configFolderPath, err = GetDefaultConfigPath() - if err != nil { - return err - } + envConfigPath := os.Getenv("OPENIMCONFIG") + if envConfigPath != "" { + configFolderPath = envConfigPath + } else { + configFolderPath = GetDefaultConfigPath() } } - // Initialize the main configuration - if err := initConfig(&Config, FileName, configFolderPath); err != nil { + if err := initConfig(config, FileName, configFolderPath); err != nil { return err } - // Initialize the notification configuration - if err := initConfig(&Config.Notification, NotificationFileName, configFolderPath); err != nil { - return err - } - - return nil + return initConfig(&config.Notification, NotificationFileName, configFolderPath) } diff --git a/pkg/common/config/parse_test.go b/pkg/common/config/parse_test.go index b980de7bd..84dee1165 100644 --- a/pkg/common/config/parse_test.go +++ b/pkg/common/config/parse_test.go @@ -103,13 +103,14 @@ func TestInitConfig(t *testing.T) { tests := []struct { name string args args + config *GlobalConfig wantErr bool }{ // TODO: Add test cases. } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := InitConfig(tt.args.configFolderPath); (err != nil) != tt.wantErr { + if err := InitConfig(tt.config, tt.args.configFolderPath); (err != nil) != tt.wantErr { t.Errorf("InitConfig() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/pkg/common/db/cache/init_redis.go b/pkg/common/db/cache/init_redis.go index b81edf9e5..8f4e2c592 100644 --- a/pkg/common/db/cache/init_redis.go +++ b/pkg/common/db/cache/init_redis.go @@ -38,34 +38,34 @@ const ( ) // NewRedis Initialize redis connection. -func NewRedis() (redis.UniversalClient, error) { +func NewRedis(config *config.GlobalConfig) (redis.UniversalClient, error) { if redisClient != nil { return redisClient, nil } // Read configuration from environment variables - overrideConfigFromEnv() + overrideConfigFromEnv(config) - if len(config.Config.Redis.Address) == 0 { - return nil, errs.Wrap(errors.New("redis address is empty"), "Redis configuration error") + if len(config.Redis.Address) == 0 { + return nil, errs.Wrap(errors.New("redis address is empty")) } specialerror.AddReplace(redis.Nil, errs.ErrRecordNotFound) var rdb redis.UniversalClient - if len(config.Config.Redis.Address) > 1 || config.Config.Redis.ClusterMode { + if len(config.Redis.Address) > 1 || config.Redis.ClusterMode { rdb = redis.NewClusterClient(&redis.ClusterOptions{ - Addrs: config.Config.Redis.Address, - Username: config.Config.Redis.Username, - Password: config.Config.Redis.Password, // no password set + Addrs: config.Redis.Address, + Username: config.Redis.Username, + Password: config.Redis.Password, // no password set PoolSize: 50, MaxRetries: maxRetry, }) } else { rdb = redis.NewClient(&redis.Options{ - Addr: config.Config.Redis.Address[0], - Username: config.Config.Redis.Username, - Password: config.Config.Redis.Password, // no password set - DB: 0, // use default DB - PoolSize: 100, // connection pool size + Addr: config.Redis.Address[0], + Username: config.Redis.Username, + Password: config.Redis.Password, + DB: 0, // use default DB + PoolSize: 100, // connection pool size MaxRetries: maxRetry, }) } @@ -75,33 +75,33 @@ func NewRedis() (redis.UniversalClient, error) { defer cancel() err = rdb.Ping(ctx).Err() if err != nil { - uriFormat := "address:%v, username:%s, clusterMode:%t, enablePipeline:%t" - errMsg := fmt.Sprintf(uriFormat, config.Config.Redis.Address, config.Config.Redis.Username, config.Config.Redis.ClusterMode, config.Config.Redis.EnablePipeline) - return nil, errs.Wrap(err, "Redis connection failed: %s", errMsg) + errMsg := fmt.Sprintf("address:%s, username:%s, password:%s, clusterMode:%t, enablePipeline:%t", config.Redis.Address, config.Redis.Username, + config.Redis.Password, config.Redis.ClusterMode, config.Redis.EnablePipeline) + return nil, errs.Wrap(err, errMsg) } redisClient = rdb return rdb, err } // overrideConfigFromEnv overrides configuration fields with environment variables if present. -func overrideConfigFromEnv() { +func overrideConfigFromEnv(config *config.GlobalConfig) { if envAddr := os.Getenv("REDIS_ADDRESS"); envAddr != "" { if envPort := os.Getenv("REDIS_PORT"); envPort != "" { addresses := strings.Split(envAddr, ",") for i, addr := range addresses { addresses[i] = addr + ":" + envPort } - config.Config.Redis.Address = addresses + config.Redis.Address = addresses } else { - config.Config.Redis.Address = strings.Split(envAddr, ",") + config.Redis.Address = strings.Split(envAddr, ",") } } if envUser := os.Getenv("REDIS_USERNAME"); envUser != "" { - config.Config.Redis.Username = envUser + config.Redis.Username = envUser } if envPass := os.Getenv("REDIS_PASSWORD"); envPass != "" { - config.Config.Redis.Password = envPass + config.Redis.Password = envPass } } diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 4c25754d6..1fbb6c3b6 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -18,13 +18,16 @@ import ( "context" "encoding/json" "errors" + "fmt" "time" + "github.com/OpenIMSDK/tools/mw/specialerror" + + "github.com/dtm-labs/rockscache" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mw/specialerror" "github.com/OpenIMSDK/tools/utils" - "github.com/dtm-labs/rockscache" ) const ( @@ -128,7 +131,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) { t, err = fn(ctx) if err != nil { - return "", err + return "", errs.Wrap(err) } bs, err := json.Marshal(t) if err != nil { @@ -139,7 +142,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin return string(bs), nil }) if err != nil { - return t, err + return t, errs.Wrap(err) } if write { return t, nil @@ -149,8 +152,8 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin } err = json.Unmarshal([]byte(v), &t) if err != nil { - log.ZError(ctx, "cache json.Unmarshal failed", err, "key", key, "value", v, "expire", expire) - return t, errs.Wrap(err, "unmarshal failed") + errInfo := fmt.Sprintf("cache json.Unmarshal failed, key:%s, value:%s, expire:%s", key, v, expire) + return t, errs.Wrap(err, errInfo) } return t, nil @@ -203,7 +206,7 @@ func batchGetCache2[T any, K comparable]( fns func(ctx context.Context, key K) (T, error), ) ([]T, error) { if len(keys) == 0 { - return nil, nil + return nil, errs.ErrArgs.Wrap("groupID is empty") } res := make([]T, 0, len(keys)) for _, key := range keys { @@ -214,7 +217,7 @@ func batchGetCache2[T any, K comparable]( if errs.ErrRecordNotFound.Is(specialerror.ErrCode(errs.Unwrap(err))) { continue } - return nil, err + return nil, errs.Wrap(err) } res = append(res, val) } diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 889f36baa..1266875f1 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -121,13 +121,14 @@ type MsgModel interface { UnLockMessageTypeKey(ctx context.Context, clientMsgID string, TypeKey string) error } -func NewMsgCacheModel(client redis.UniversalClient) MsgModel { - return &msgCache{rdb: client} +func NewMsgCacheModel(client redis.UniversalClient, config *config.GlobalConfig) MsgModel { + return &msgCache{rdb: client, config: config} } type msgCache struct { metaCache - rdb redis.UniversalClient + rdb redis.UniversalClient + config *config.GlobalConfig } func (c *msgCache) getMaxSeqKey(conversationID string) string { @@ -315,7 +316,7 @@ func (c *msgCache) allMessageCacheKey(conversationID string) string { } func (c *msgCache) GetMessagesBySeq(ctx context.Context, conversationID string, seqs []int64) (seqMsgs []*sdkws.MsgData, failedSeqs []int64, err error) { - if config.Config.Redis.EnablePipeline { + if c.config.Redis.EnablePipeline { return c.PipeGetMessagesBySeq(ctx, conversationID, seqs) } @@ -416,7 +417,7 @@ func (c *msgCache) ParallelGetMessagesBySeq(ctx context.Context, conversationID } func (c *msgCache) SetMessageToCache(ctx context.Context, conversationID string, msgs []*sdkws.MsgData) (int, error) { - if config.Config.Redis.EnablePipeline { + if c.config.Redis.EnablePipeline { return c.PipeSetMessageToCache(ctx, conversationID, msgs) } return c.ParallelSetMessageToCache(ctx, conversationID, msgs) @@ -431,7 +432,7 @@ func (c *msgCache) PipeSetMessageToCache(ctx context.Context, conversationID str } key := c.getMessageCacheKey(conversationID, msg.Seq) - _ = pipe.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second) + _ = pipe.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second) } results, err := pipe.Exec(ctx) @@ -461,7 +462,7 @@ func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID } key := c.getMessageCacheKey(conversationID, msg.Seq) - if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil { + if err := c.rdb.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil { return errs.Wrap(err) } return nil @@ -496,10 +497,10 @@ func (c *msgCache) UserDeleteMsgs(ctx context.Context, conversationID string, se if err != nil { return errs.Wrap(err) } - if err := c.rdb.Expire(ctx, delUserListKey, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil { + if err := c.rdb.Expire(ctx, delUserListKey, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil { return errs.Wrap(err) } - if err := c.rdb.Expire(ctx, userDelListKey, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil { + if err := c.rdb.Expire(ctx, userDelListKey, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil { return errs.Wrap(err) } } @@ -604,7 +605,7 @@ func (c *msgCache) DelUserDeleteMsgsList(ctx context.Context, conversationID str } func (c *msgCache) DeleteMessages(ctx context.Context, conversationID string, seqs []int64) error { - if config.Config.Redis.EnablePipeline { + if c.config.Redis.EnablePipeline { return c.PipeDeleteMessages(ctx, conversationID, seqs) } @@ -686,7 +687,7 @@ func (c *msgCache) DelMsgFromCache(ctx context.Context, userID string, seqs []in if err != nil { return errs.Wrap(err) } - if err := c.rdb.Set(ctx, key, s, time.Duration(config.Config.MsgCacheTimeout)*time.Second).Err(); err != nil { + if err := c.rdb.Set(ctx, key, s, time.Duration(c.config.MsgCacheTimeout)*time.Second).Err(); err != nil { return errs.Wrap(err) } } diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index c6c6966f3..34c220624 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -22,12 +22,16 @@ import ( "strconv" "time" + relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/log" + "github.com/dtm-labs/rockscache" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/redis/go-redis/v9" ) @@ -62,7 +66,11 @@ type UserCacheRedis struct { rcClient *rockscache.Client } -func NewUserCacheRedis(rdb redis.UniversalClient, userDB relationtb.UserModelInterface, options rockscache.Options) UserCache { +func NewUserCacheRedis( + rdb redis.UniversalClient, + userDB relationtb.UserModelInterface, + options rockscache.Options, +) UserCache { rcClient := rockscache.NewClient(rdb, options) return &UserCacheRedis{ @@ -193,13 +201,13 @@ func (u *UserCacheRedis) SetUserStatus(ctx context.Context, userID string, statu Status: constant.Online, PlatformIDs: []int32{platformID}, } - jsonData, err2 := json.Marshal(&onlineStatus) - if err2 != nil { - return errs.Wrap(err2) + jsonData, err := json.Marshal(&onlineStatus) + if err != nil { + return errs.Wrap(err) } - _, err2 = u.rdb.HSet(ctx, key, userID, string(jsonData)).Result() - if err2 != nil { - return errs.Wrap(err2) + _, err = u.rdb.HSet(ctx, key, userID, string(jsonData)).Result() + if err != nil { + return errs.Wrap(err) } u.rdb.Expire(ctx, key, userOlineStatusExpireTime) @@ -273,9 +281,9 @@ func (u *UserCacheRedis) refreshStatusOffline(ctx context.Context, userID string func (u *UserCacheRedis) refreshStatusOnline(ctx context.Context, userID string, platformID int32, isNil bool, err error, result, key string) error { var onlineStatus user.OnlineStatus if !isNil { - err2 := json.Unmarshal([]byte(result), &onlineStatus) - if err2 != nil { - return errs.Wrap(err, "json.Unmarshal failed") + err := json.Unmarshal([]byte(result), &onlineStatus) + if err != nil { + return errs.Wrap(err) } onlineStatus.PlatformIDs = RemoveRepeatedElementsInList(append(onlineStatus.PlatformIDs, platformID)) } else { diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index dfd7b3e78..19bea5981 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -16,6 +16,7 @@ package controller import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" @@ -33,14 +34,14 @@ type AuthDatabase interface { } type authDatabase struct { - cache cache.MsgModel - + cache cache.MsgModel accessSecret string accessExpire int64 + config *config.GlobalConfig } -func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int64) AuthDatabase { - return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire} +func NewAuthDatabase(cache cache.MsgModel, accessSecret string, accessExpire int64, config *config.GlobalConfig) AuthDatabase { + return &authDatabase{cache: cache, accessSecret: accessSecret, accessExpire: accessExpire, config: config} } // If the result is empty. @@ -56,7 +57,7 @@ func (a *authDatabase) CreateToken(ctx context.Context, userID string, platformI } var deleteTokenKey []string for k, v := range tokens { - _, err = tokenverify.GetClaimFromToken(k, authverify.Secret()) + _, err = tokenverify.GetClaimFromToken(k, authverify.Secret(a.config.Secret)) if err != nil || v != constant.NormalToken { deleteTokenKey = append(deleteTokenKey, k) } diff --git a/pkg/common/db/controller/msg.go b/pkg/common/db/controller/msg.go index 56cb52703..ccf209b7a 100644 --- a/pkg/common/db/controller/msg.go +++ b/pkg/common/db/controller/msg.go @@ -120,16 +120,33 @@ type CommonMsgDatabase interface { ConvertMsgsDocLen(ctx context.Context, conversationIDs []string) } -func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel) (CommonMsgDatabase, error) { - producerToRedis, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.LatestMsgToRedis.Topic) +func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheModel cache.MsgModel, config *config.GlobalConfig) (CommonMsgDatabase, error) { + producerConfig := &kafka.ProducerConfig{ + ProducerAck: config.Kafka.ProducerAck, + CompressType: config.Kafka.CompressType, + Username: config.Kafka.Username, + Password: config.Kafka.Password, + } + + var tlsConfig *kafka.TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &kafka.TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: false, + } + } + producerToRedis, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.LatestMsgToRedis.Topic, producerConfig, tlsConfig) if err != nil { return nil, err } - producerToMongo, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToMongo.Topic) + producerToMongo, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.MsgToMongo.Topic, producerConfig, tlsConfig) if err != nil { return nil, err } - producerToPush, err := kafka.NewKafkaProducer(config.Config.Kafka.Addr, config.Config.Kafka.MsgToPush.Topic) + producerToPush, err := kafka.NewKafkaProducer(config.Kafka.Addr, config.Kafka.MsgToPush.Topic, producerConfig, tlsConfig) if err != nil { return nil, err } @@ -142,10 +159,10 @@ func NewCommonMsgDatabase(msgDocModel unrelationtb.MsgDocModelInterface, cacheMo }, nil } -func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database) (CommonMsgDatabase, error) { - cacheModel := cache.NewMsgCacheModel(rdb) +func InitCommonMsgDatabase(rdb redis.UniversalClient, database *mongo.Database, config *config.GlobalConfig) (CommonMsgDatabase, error) { + cacheModel := cache.NewMsgCacheModel(rdb, config) msgDocModel := unrelation.NewMsgMongoDriver(database) - return NewCommonMsgDatabase(msgDocModel, cacheModel) + return NewCommonMsgDatabase(msgDocModel, cacheModel, config) } type commonMsgDatabase struct { @@ -397,9 +414,9 @@ func (db *commonMsgDatabase) BatchInsertChat2Cache(ctx context.Context, conversa log.ZError(ctx, "db.cache.SetMaxSeq error", err, "conversationID", conversationID) prommetrics.SeqSetFailedCounter.Inc() } - err2 := db.cache.SetHasReadSeqs(ctx, conversationID, userSeqMap) + err = db.cache.SetHasReadSeqs(ctx, conversationID, userSeqMap) if err != nil { - log.ZError(ctx, "SetHasReadSeqs error", err2, "userSeqMap", userSeqMap, "conversationID", conversationID) + log.ZError(ctx, "SetHasReadSeqs error", err, "userSeqMap", userSeqMap, "conversationID", conversationID) prommetrics.SeqSetFailedCounter.Inc() } return lastMaxSeq, isNew, errs.Wrap(err) diff --git a/pkg/common/db/controller/msg_test.go b/pkg/common/db/controller/msg_test.go index 70c055bf3..4c2ab20da 100644 --- a/pkg/common/db/controller/msg_test.go +++ b/pkg/common/db/controller/msg_test.go @@ -33,27 +33,28 @@ import ( ) func Test_BatchInsertChat2DB(t *testing.T) { - config.Config.Mongo.Address = []string{"192.168.44.128:37017"} - // config.Config.Mongo.Timeout = 60 - config.Config.Mongo.Database = "openIM" - // config.Config.Mongo.Source = "admin" - config.Config.Mongo.Username = "root" - config.Config.Mongo.Password = "openIM123" - config.Config.Mongo.MaxPoolSize = 100 - config.Config.RetainChatRecords = 3650 - config.Config.ChatRecordsClearTime = "0 2 * * 3" - - mongo, err := unrelation.NewMongo() + conf := config.NewGlobalConfig() + conf.Mongo.Address = []string{"192.168.44.128:37017"} + // conf.Mongo.Timeout = 60 + conf.Mongo.Database = "openIM" + // conf.Mongo.Source = "admin" + conf.Mongo.Username = "root" + conf.Mongo.Password = "openIM123" + conf.Mongo.MaxPoolSize = 100 + conf.RetainChatRecords = 3650 + conf.ChatRecordsClearTime = "0 2 * * 3" + + mongo, err := unrelation.NewMongo(conf) if err != nil { t.Fatal(err) } - err = mongo.GetDatabase().Client().Ping(context.Background(), nil) + err = mongo.GetDatabase(conf.Mongo.Database).Client().Ping(context.Background(), nil) if err != nil { panic(err) } db := &commonMsgDatabase{ - msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase()), + msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase(conf.Mongo.Database)), } //ctx := context.Background() @@ -70,7 +71,7 @@ func Test_BatchInsertChat2DB(t *testing.T) { //} _ = db.BatchInsertChat2DB - c := mongo.GetDatabase().Collection("msg") + c := mongo.GetDatabase(conf.Mongo.Database).Collection("msg") ch := make(chan int) rand.Seed(time.Now().UnixNano()) @@ -144,26 +145,27 @@ func Test_BatchInsertChat2DB(t *testing.T) { } func GetDB() *commonMsgDatabase { - config.Config.Mongo.Address = []string{"203.56.175.233:37017"} - // config.Config.Mongo.Timeout = 60 - config.Config.Mongo.Database = "openim_v3" - // config.Config.Mongo.Source = "admin" - config.Config.Mongo.Username = "root" - config.Config.Mongo.Password = "openIM123" - config.Config.Mongo.MaxPoolSize = 100 - config.Config.RetainChatRecords = 3650 - config.Config.ChatRecordsClearTime = "0 2 * * 3" - - mongo, err := unrelation.NewMongo() + conf := config.NewGlobalConfig() + conf.Mongo.Address = []string{"203.56.175.233:37017"} + // conf.Mongo.Timeout = 60 + conf.Mongo.Database = "openim_v3" + // conf.Mongo.Source = "admin" + conf.Mongo.Username = "root" + conf.Mongo.Password = "openIM123" + conf.Mongo.MaxPoolSize = 100 + conf.RetainChatRecords = 3650 + conf.ChatRecordsClearTime = "0 2 * * 3" + + mongo, err := unrelation.NewMongo(conf) if err != nil { panic(err) } - err = mongo.GetDatabase().Client().Ping(context.Background(), nil) + err = mongo.GetDatabase(conf.Mongo.Database).Client().Ping(context.Background(), nil) if err != nil { panic(err) } return &commonMsgDatabase{ - msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase()), + msgDocDatabase: unrelation.NewMsgMongoDriver(mongo.GetDatabase(conf.Mongo.Database)), } } diff --git a/pkg/common/db/s3/aws/aws.go b/pkg/common/db/s3/aws/aws.go deleted file mode 100644 index dd54ed155..000000000 --- a/pkg/common/db/s3/aws/aws.go +++ /dev/null @@ -1,275 +0,0 @@ -// Copyright © 2023 OpenIM. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// docURL: https://docs.aws.amazon.com/AmazonS3/latest/API/Welcome.html - -package aws - -import ( - "context" - "errors" - "fmt" - "time" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - sdk "github.com/aws/aws-sdk-go/service/s3" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" -) - -const ( - minPartSize int64 = 1024 * 1024 * 1 // 1MB - maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB - maxNumSize int64 = 10000 -) - -// const ( -// imagePng = "png" -// imageJpg = "jpg" -// imageJpeg = "jpeg" -// imageGif = "gif" -// imageWebp = "webp" -// ) - -// const successCode = http.StatusOK - -// const ( -// videoSnapshotImagePng = "png" -// videoSnapshotImageJpg = "jpg" -// ) - -func NewAWS() (s3.Interface, error) { - conf := config.Config.Object.Aws - credential := credentials.NewStaticCredentials( - conf.AccessKeyID, // accessKey - conf.AccessKeySecret, // secretKey - "") // stoken - - sess, err := session.NewSession(&aws.Config{ - Region: aws.String(conf.Region), // The area where the bucket is located - Credentials: credential, - }) - - if err != nil { - return nil, err - } - return &Aws{ - bucket: conf.Bucket, - client: sdk.New(sess), - credential: credential, - }, nil -} - -type Aws struct { - bucket string - client *sdk.S3 - credential *credentials.Credentials -} - -func (a *Aws) Engine() string { - return "aws" -} - -func (a *Aws) InitiateMultipartUpload(ctx context.Context, name string) (*s3.InitiateMultipartUploadResult, error) { - input := &sdk.CreateMultipartUploadInput{ - Bucket: aws.String(a.bucket), // TODO: To be verified whether it is required - Key: aws.String(name), - } - result, err := a.client.CreateMultipartUploadWithContext(ctx, input) - if err != nil { - return nil, err - } - return &s3.InitiateMultipartUploadResult{ - Bucket: *result.Bucket, - Key: *result.Key, - UploadID: *result.UploadId, - }, nil -} - -func (a *Aws) CompleteMultipartUpload(ctx context.Context, uploadID string, name string, parts []s3.Part) (*s3.CompleteMultipartUploadResult, error) { - sdkParts := make([]*sdk.CompletedPart, len(parts)) - for i, part := range parts { - sdkParts[i] = &sdk.CompletedPart{ - ETag: aws.String(part.ETag), - PartNumber: aws.Int64(int64(part.PartNumber)), - } - } - input := &sdk.CompleteMultipartUploadInput{ - Bucket: aws.String(a.bucket), // TODO: To be verified whether it is required - Key: aws.String(name), - UploadId: aws.String(uploadID), - MultipartUpload: &sdk.CompletedMultipartUpload{ - Parts: sdkParts, - }, - } - result, err := a.client.CompleteMultipartUploadWithContext(ctx, input) - if err != nil { - return nil, err - } - return &s3.CompleteMultipartUploadResult{ - Location: *result.Location, - Bucket: *result.Bucket, - Key: *result.Key, - ETag: *result.ETag, - }, nil -} - -func (a *Aws) PartSize(ctx context.Context, size int64) (int64, error) { - if size <= 0 { - return 0, errors.New("size must be greater than 0") - } - if size > maxPartSize*maxNumSize { - return 0, fmt.Errorf("AWS size must be less than the maximum allowed limit") - } - if size <= minPartSize*maxNumSize { - return minPartSize, nil - } - partSize := size / maxNumSize - if size%maxNumSize != 0 { - partSize++ - } - return partSize, nil -} - -func (a *Aws) DeleteObject(ctx context.Context, name string) error { - _, err := a.client.DeleteObjectWithContext(ctx, &sdk.DeleteObjectInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(name), - }) - return err -} - -func (a *Aws) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyObjectInfo, error) { - result, err := a.client.CopyObjectWithContext(ctx, &sdk.CopyObjectInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(dst), - CopySource: aws.String(src), - }) - if err != nil { - return nil, err - } - return &s3.CopyObjectInfo{ - ETag: *result.CopyObjectResult.ETag, - Key: dst, - }, nil -} - -func (a *Aws) IsNotFound(err error) bool { - if err == nil { - return false - } - if aerr, ok := err.(awserr.Error); ok { - switch aerr.Code() { - case sdk.ErrCodeNoSuchKey: - return true - default: - return false - } - } - return false -} - -func (a *Aws) AbortMultipartUpload(ctx context.Context, uploadID string, name string) error { - _, err := a.client.AbortMultipartUploadWithContext(ctx, &sdk.AbortMultipartUploadInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(name), - UploadId: aws.String(uploadID), - }) - return err -} - -func (a *Aws) ListUploadedParts(ctx context.Context, uploadID string, name string, partNumberMarker int, maxParts int) (*s3.ListUploadedPartsResult, error) { - result, err := a.client.ListPartsWithContext(ctx, &sdk.ListPartsInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(name), - UploadId: aws.String(uploadID), - MaxParts: aws.Int64(int64(maxParts)), - PartNumberMarker: aws.Int64(int64(partNumberMarker)), - }) - if err != nil { - return nil, err - } - parts := make([]s3.UploadedPart, len(result.Parts)) - for i, part := range result.Parts { - parts[i] = s3.UploadedPart{ - PartNumber: int(*part.PartNumber), - LastModified: *part.LastModified, - Size: *part.Size, - ETag: *part.ETag, - } - } - return &s3.ListUploadedPartsResult{ - Key: *result.Key, - UploadID: *result.UploadId, - NextPartNumberMarker: int(*result.NextPartNumberMarker), - MaxParts: int(*result.MaxParts), - UploadedParts: parts, - }, nil -} - -func (a *Aws) PartLimit() *s3.PartLimit { - return &s3.PartLimit{ - MinPartSize: minPartSize, - MaxPartSize: maxPartSize, - MaxNumSize: maxNumSize, - } -} - -func (a *Aws) PresignedPutObject(ctx context.Context, name string, expire time.Duration) (string, error) { - req, _ := a.client.PutObjectRequest(&sdk.PutObjectInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(name), - }) - url, err := req.Presign(expire) - if err != nil { - return "", err - } - return url, nil -} - -func (a *Aws) StatObject(ctx context.Context, name string) (*s3.ObjectInfo, error) { - result, err := a.client.GetObjectWithContext(ctx, &sdk.GetObjectInput{ - Bucket: aws.String(a.bucket), - Key: aws.String(name), - }) - if err != nil { - return nil, err - } - res := &s3.ObjectInfo{ - Key: name, - ETag: *result.ETag, - Size: *result.ContentLength, - LastModified: *result.LastModified, - } - return res, nil -} - -// AccessURL todo. -func (a *Aws) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { - // todo - return "", nil -} - -func (a *Aws) FormData(ctx context.Context, name string, size int64, contentType string, duration time.Duration) (*s3.FormData, error) { - // todo - return nil, nil -} - -func (a *Aws) AuthSign(ctx context.Context, uploadID string, name string, expire time.Duration, partNumbers []int) (*s3.AuthSignResult, error) { - // todo - return nil, nil -} diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go index 619f142ab..a7c26fcc1 100644 --- a/pkg/common/db/s3/cos/cos.go +++ b/pkg/common/db/s3/cos/cos.go @@ -23,13 +23,13 @@ import ( "encoding/json" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "net/http" "net/url" "strconv" "strings" "time" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/tencentyun/cos-go-sdk-v5" ) @@ -50,13 +50,15 @@ const ( const successCode = http.StatusOK -const ( -// videoSnapshotImagePng = "png" -// videoSnapshotImageJpg = "jpg" -) +type Config struct { + BucketURL string + SecretID string + SecretKey string + SessionToken string + PublicRead bool +} -func NewCos() (s3.Interface, error) { - conf := config.Config.Object.Cos +func NewCos(conf Config) (s3.Interface, error) { u, err := url.Parse(conf.BucketURL) if err != nil { panic(err) @@ -69,6 +71,7 @@ func NewCos() (s3.Interface, error) { }, }) return &Cos{ + publicRead: conf.PublicRead, copyURL: u.Host + "/", client: client, credential: client.GetCredential(), @@ -76,6 +79,7 @@ func NewCos() (s3.Interface, error) { } type Cos struct { + publicRead bool copyURL string client *cos.Client credential *cos.Credential @@ -226,7 +230,7 @@ func (c *Cos) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyO } func (c *Cos) IsNotFound(err error) bool { - switch e := err.(type) { + switch e := errs.Unwrap(err).(type) { case *cos.ErrorResponse: return e.Response.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" default: @@ -327,7 +331,7 @@ func (c *Cos) AccessURL(ctx context.Context, name string, expire time.Duration, } func (c *Cos) getPresignedURL(ctx context.Context, name string, expire time.Duration, opt *cos.PresignedURLOptions) (*url.URL, error) { - if !config.Config.Object.Cos.PublicRead { + if !c.publicRead { return c.client.Object.GetPresignedURL(ctx, http.MethodGet, name, c.credential.SecretID, c.credential.SecretKey, expire, opt) } return c.client.Object.GetObjectURL(name), nil diff --git a/pkg/common/db/s3/minio/image.go b/pkg/common/db/s3/minio/image.go index f363f94b1..71db1ea51 100644 --- a/pkg/common/db/s3/minio/image.go +++ b/pkg/common/db/s3/minio/image.go @@ -42,51 +42,79 @@ func ImageWidthHeight(img image.Image) (int, int) { return bounds.X, bounds.Y } -// resizeImage resizes an image to a specified maximum width and height, maintaining the aspect ratio. -// If both maxWidth and maxHeight are set to 0, the original image is returned. -// If both are non-zero, the image is scaled to fit within the constraints while maintaining aspect ratio. -// If only one of maxWidth or maxHeight is non-zero, the image is scaled accordingly. func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { bounds := img.Bounds() - imgWidth, imgHeight := bounds.Dx(), bounds.Dy() + imgWidth := bounds.Max.X + imgHeight := bounds.Max.Y - // Return original image if no resizing is needed. + // 计算缩放比例 + scaleWidth := float64(maxWidth) / float64(imgWidth) + scaleHeight := float64(maxHeight) / float64(imgHeight) + + // 如果都为0,则不缩放,返回原始图片 if maxWidth == 0 && maxHeight == 0 { return img } - var scale float64 = 1 + // 如果宽度和高度都大于0,则选择较小的缩放比例,以保持宽高比 if maxWidth > 0 && maxHeight > 0 { - scaleWidth := float64(maxWidth) / float64(imgWidth) - scaleHeight := float64(maxHeight) / float64(imgHeight) - // Choose the smaller scale to fit both constraints. - scale = min(scaleWidth, scaleHeight) - } else if maxWidth > 0 { - scale = float64(maxWidth) / float64(imgWidth) - } else if maxHeight > 0 { - scale = float64(maxHeight) / float64(imgHeight) + scale := scaleWidth + if scaleHeight < scaleWidth { + scale = scaleHeight + } + + // 计算缩略图尺寸 + thumbnailWidth := int(float64(imgWidth) * scale) + thumbnailHeight := int(float64(imgHeight) * scale) + + // 使用"image"库的Resample方法生成缩略图 + thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) + for y := 0; y < thumbnailHeight; y++ { + for x := 0; x < thumbnailWidth; x++ { + srcX := int(float64(x) / scale) + srcY := int(float64(y) / scale) + thumbnail.Set(x, y, img.At(srcX, srcY)) + } + } + + return thumbnail } - newWidth := int(float64(imgWidth) * scale) - newHeight := int(float64(imgHeight) * scale) + // 如果只指定了宽度或高度,则根据最大不超过的规则生成缩略图 + if maxWidth > 0 { + thumbnailWidth := maxWidth + thumbnailHeight := int(float64(imgHeight) * scaleWidth) - // Resize the image by creating a new image and manually copying pixels. - thumbnail := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight)) - for y := 0; y < newHeight; y++ { - for x := 0; x < newWidth; x++ { - srcX := int(float64(x) / scale) - srcY := int(float64(y) / scale) - thumbnail.Set(x, y, img.At(srcX, srcY)) + // 使用"image"库的Resample方法生成缩略图 + thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) + for y := 0; y < thumbnailHeight; y++ { + for x := 0; x < thumbnailWidth; x++ { + srcX := int(float64(x) / scaleWidth) + srcY := int(float64(y) / scaleWidth) + thumbnail.Set(x, y, img.At(srcX, srcY)) + } } + + return thumbnail } - return thumbnail -} + if maxHeight > 0 { + thumbnailWidth := int(float64(imgWidth) * scaleHeight) + thumbnailHeight := maxHeight -// min returns the smaller of x or y. -func min(x, y float64) float64 { - if x < y { - return x + // 使用"image"库的Resample方法生成缩略图 + thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) + for y := 0; y < thumbnailHeight; y++ { + for x := 0; x < thumbnailWidth; x++ { + srcX := int(float64(x) / scaleHeight) + srcY := int(float64(y) / scaleHeight) + thumbnail.Set(x, y, img.At(srcX, srcY)) + } + } + + return thumbnail } - return y + + // 默认情况下,返回原始图片 + return img } diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index 1eb3257e1..cd77948d4 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "io" "net/http" "net/url" @@ -33,7 +34,6 @@ import ( "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" "github.com/minio/minio-go/v7/pkg/signer" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) @@ -43,7 +43,7 @@ const ( ) const ( - minPartSize int64 = 1024 * 1024 * 5 // 1MB + minPartSize int64 = 1024 * 1024 * 5 // 5MB maxPartSize int64 = 1024 * 1024 * 1024 * 5 // 5GB maxNumSize int64 = 10000 ) @@ -57,13 +57,23 @@ const ( const successCode = http.StatusOK -func NewMinio(cache cache.MinioCache) (s3.Interface, error) { - u, err := url.Parse(config.Config.Object.Minio.Endpoint) +type Config struct { + Bucket string + Endpoint string + AccessKeyID string + SecretAccessKey string + SessionToken string + SignEndpoint string + PublicRead bool +} + +func NewMinio(cache cache.MinioCache, conf Config) (s3.Interface, error) { + u, err := url.Parse(conf.Endpoint) if err != nil { return nil, err } opts := &minio.Options{ - Creds: credentials.NewStaticV4(config.Config.Object.Minio.AccessKeyID, config.Config.Object.Minio.SecretAccessKey, config.Config.Object.Minio.SessionToken), + Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken), Secure: u.Scheme == "https", } client, err := minio.New(u.Host, opts) @@ -71,26 +81,27 @@ func NewMinio(cache cache.MinioCache) (s3.Interface, error) { return nil, err } m := &Minio{ - bucket: config.Config.Object.Minio.Bucket, + conf: conf, + bucket: conf.Bucket, core: &minio.Core{Client: client}, lock: &sync.Mutex{}, init: false, cache: cache, } - if config.Config.Object.Minio.SignEndpoint == "" || config.Config.Object.Minio.SignEndpoint == config.Config.Object.Minio.Endpoint { + if conf.SignEndpoint == "" || conf.SignEndpoint == conf.Endpoint { m.opts = opts m.sign = m.core.Client m.prefix = u.Path u.Path = "" - config.Config.Object.Minio.Endpoint = u.String() - m.signEndpoint = config.Config.Object.Minio.Endpoint + conf.Endpoint = u.String() + m.signEndpoint = conf.Endpoint } else { - su, err := url.Parse(config.Config.Object.Minio.SignEndpoint) + su, err := url.Parse(conf.SignEndpoint) if err != nil { return nil, err } m.opts = &minio.Options{ - Creds: credentials.NewStaticV4(config.Config.Object.Minio.AccessKeyID, config.Config.Object.Minio.SecretAccessKey, config.Config.Object.Minio.SessionToken), + Creds: credentials.NewStaticV4(conf.AccessKeyID, conf.SecretAccessKey, conf.SessionToken), Secure: su.Scheme == "https", } m.sign, err = minio.New(su.Host, m.opts) @@ -99,8 +110,8 @@ func NewMinio(cache cache.MinioCache) (s3.Interface, error) { } m.prefix = su.Path su.Path = "" - config.Config.Object.Minio.SignEndpoint = su.String() - m.signEndpoint = config.Config.Object.Minio.SignEndpoint + conf.SignEndpoint = su.String() + m.signEndpoint = conf.SignEndpoint } ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -111,6 +122,7 @@ func NewMinio(cache cache.MinioCache) (s3.Interface, error) { } type Minio struct { + conf Config bucket string signEndpoint string location string @@ -132,31 +144,30 @@ func (m *Minio) initMinio(ctx context.Context) error { if m.init { return nil } - conf := config.Config.Object.Minio - exists, err := m.core.Client.BucketExists(ctx, conf.Bucket) + exists, err := m.core.Client.BucketExists(ctx, m.conf.Bucket) if err != nil { return fmt.Errorf("check bucket exists error: %w", err) } if !exists { - if err = m.core.Client.MakeBucket(ctx, conf.Bucket, minio.MakeBucketOptions{}); err != nil { + if err = m.core.Client.MakeBucket(ctx, m.conf.Bucket, minio.MakeBucketOptions{}); err != nil { return fmt.Errorf("make bucket error: %w", err) } } - if conf.PublicRead { + if m.conf.PublicRead { policy := fmt.Sprintf( `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject","s3:PutObject"],"Effect": "Allow","Principal": {"AWS": ["*"]},"Resource": ["arn:aws:s3:::%s/*"],"Sid": ""}]}`, - conf.Bucket, + m.conf.Bucket, ) - if err = m.core.Client.SetBucketPolicy(ctx, conf.Bucket, policy); err != nil { + if err = m.core.Client.SetBucketPolicy(ctx, m.conf.Bucket, policy); err != nil { return err } } - m.location, err = m.core.Client.GetBucketLocation(ctx, conf.Bucket) + m.location, err = m.core.Client.GetBucketLocation(ctx, m.conf.Bucket) if err != nil { return err } func() { - if conf.SignEndpoint == "" || conf.SignEndpoint == conf.Endpoint { + if m.conf.SignEndpoint == "" || m.conf.SignEndpoint == m.conf.Endpoint { return } defer func() { @@ -176,7 +187,7 @@ func (m *Minio) initMinio(ctx context.Context) error { blc := reflect.ValueOf(m.sign).Elem().FieldByName("bucketLocCache") vblc := reflect.New(reflect.PtrTo(blc.Type())) *(*unsafe.Pointer)(vblc.UnsafePointer()) = unsafe.Pointer(blc.UnsafeAddr()) - vblc.Elem().Elem().Interface().(interface{ Set(string, string) }).Set(conf.Bucket, m.location) + vblc.Elem().Elem().Interface().(interface{ Set(string, string) }).Set(m.conf.Bucket, m.location) }() m.init = true return nil @@ -341,10 +352,7 @@ func (m *Minio) CopyObject(ctx context.Context, src string, dst string) (*s3.Cop } func (m *Minio) IsNotFound(err error) bool { - if err == nil { - return false - } - switch e := err.(type) { + switch e := errs.Unwrap(err).(type) { case minio.ErrorResponse: return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" case *minio.ErrorResponse: @@ -397,7 +405,7 @@ func (m *Minio) PresignedGetObject(ctx context.Context, name string, expire time rawURL *url.URL err error ) - if config.Config.Object.Minio.PublicRead { + if m.conf.PublicRead { rawURL, err = makeTargetURL(m.sign, m.bucket, name, m.location, false, query) } else { rawURL, err = m.sign.PresignedGetObject(ctx, m.bucket, name, expire, query) diff --git a/pkg/common/db/s3/oss/oss.go b/pkg/common/db/s3/oss/oss.go index 442f4e52f..e485db277 100644 --- a/pkg/common/db/s3/oss/oss.go +++ b/pkg/common/db/s3/oss/oss.go @@ -32,7 +32,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/aliyun/aliyun-oss-go-sdk/oss" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" ) @@ -52,13 +51,17 @@ const ( const successCode = http.StatusOK -/* const ( - videoSnapshotImagePng = "png" - videoSnapshotImageJpg = "jpg" -) */ +type Config struct { + Endpoint string + Bucket string + BucketURL string + AccessKeyID string + AccessKeySecret string + SessionToken string + PublicRead bool +} -func NewOSS() (s3.Interface, error) { - conf := config.Config.Object.Oss +func NewOSS(conf Config) (s3.Interface, error) { if conf.BucketURL == "" { return nil, errs.Wrap(errors.New("bucket url is empty")) } @@ -78,6 +81,7 @@ func NewOSS() (s3.Interface, error) { bucket: bucket, credentials: client.Config.GetCredentials(), um: *(*urlMaker)(reflect.ValueOf(bucket.Client.Conn).Elem().FieldByName("url").UnsafePointer()), + publicRead: conf.PublicRead, }, nil } @@ -86,6 +90,7 @@ type OSS struct { bucket *oss.Bucket credentials oss.Credentials um urlMaker + publicRead bool } func (o *OSS) Engine() string { @@ -236,7 +241,7 @@ func (o *OSS) CopyObject(ctx context.Context, src string, dst string) (*s3.CopyO } func (o *OSS) IsNotFound(err error) bool { - switch e := err.(type) { + switch e := errs.Unwrap(err).(type) { case oss.ServiceError: return e.StatusCode == http.StatusNotFound || e.Code == "NoSuchKey" case *oss.ServiceError: @@ -282,7 +287,6 @@ func (o *OSS) ListUploadedParts(ctx context.Context, uploadID string, name strin } func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, opt *s3.AccessURLOption) (string, error) { - publicRead := config.Config.Object.Oss.PublicRead var opts []oss.Option if opt != nil { if opt.Image != nil { @@ -310,7 +314,7 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, process += ",format," + format opts = append(opts, oss.Process(process)) } - if !publicRead { + if !o.publicRead { if opt.ContentType != "" { opts = append(opts, oss.ResponseContentType(opt.ContentType)) } @@ -324,7 +328,7 @@ func (o *OSS) AccessURL(ctx context.Context, name string, expire time.Duration, } else if expire < time.Second { expire = time.Second } - if !publicRead { + if !o.publicRead { return o.bucket.SignURL(name, http.MethodGet, int64(expire/time.Second), opts...) } rawParams, err := oss.GetRawParams(opts) diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 09880fb37..363e97867 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -36,13 +36,14 @@ const ( ) type Mongo struct { - db *mongo.Client + db *mongo.Client + config *config.GlobalConfig } // NewMongo Initialize MongoDB connection. -func NewMongo() (*Mongo, error) { +func NewMongo(config *config.GlobalConfig) (*Mongo, error) { specialerror.AddReplace(mongo.ErrNoDocuments, errs.ErrRecordNotFound) - uri := buildMongoURI() + uri := buildMongoURI(config) var mongoClient *mongo.Client var err error @@ -56,7 +57,7 @@ func NewMongo() (*Mongo, error) { if err = mongoClient.Ping(ctx, nil); err != nil { return nil, errs.Wrap(err, uri) } - return &Mongo{db: mongoClient}, nil + return &Mongo{db: mongoClient, config: config}, nil } if shouldRetry(err) { time.Sleep(time.Second) // exponential backoff could be implemented here @@ -66,14 +67,14 @@ func NewMongo() (*Mongo, error) { return nil, errs.Wrap(err, uri) } -func buildMongoURI() string { +func buildMongoURI(config *config.GlobalConfig) string { uri := os.Getenv("MONGO_URI") if uri != "" { return uri } - if config.Config.Mongo.Uri != "" { - return config.Config.Mongo.Uri + if config.Mongo.Uri != "" { + return config.Mongo.Uri } username := os.Getenv("MONGO_OPENIM_USERNAME") @@ -84,21 +85,21 @@ func buildMongoURI() string { maxPoolSize := os.Getenv("MONGO_MAX_POOL_SIZE") if username == "" { - username = config.Config.Mongo.Username + username = config.Mongo.Username } if password == "" { - password = config.Config.Mongo.Password + password = config.Mongo.Password } if address == "" { - address = strings.Join(config.Config.Mongo.Address, ",") + address = strings.Join(config.Mongo.Address, ",") } else if port != "" { address = fmt.Sprintf("%s:%s", address, port) } if database == "" { - database = config.Config.Mongo.Database + database = config.Mongo.Database } if maxPoolSize == "" { - maxPoolSize = fmt.Sprint(config.Config.Mongo.MaxPoolSize) + maxPoolSize = fmt.Sprint(config.Mongo.MaxPoolSize) } uriFormat := "mongodb://%s/%s?maxPoolSize=%s" @@ -122,8 +123,8 @@ func (m *Mongo) GetClient() *mongo.Client { } // GetDatabase returns the specific database from MongoDB. -func (m *Mongo) GetDatabase() *mongo.Database { - return m.db.Database(config.Config.Mongo.Database) +func (m *Mongo) GetDatabase(database string) *mongo.Database { + return m.db.Database(database) } // CreateMsgIndex creates an index for messages in MongoDB. @@ -133,7 +134,7 @@ func (m *Mongo) CreateMsgIndex() error { // createMongoIndex creates an index in a MongoDB collection. func (m *Mongo) createMongoIndex(collection string, isUnique bool, keys ...string) error { - db := m.GetDatabase().Collection(collection) + db := m.GetDatabase(m.config.Mongo.Database).Collection(collection) opts := options.CreateIndexes().SetMaxTime(10 * time.Second) indexView := db.Indexes() diff --git a/pkg/common/discoveryregister/direct/directconn.go b/pkg/common/discoveryregister/direct/directconn.go index 2ae0de170..ced209602 100644 --- a/pkg/common/discoveryregister/direct/directconn.go +++ b/pkg/common/discoveryregister/direct/directconn.go @@ -27,17 +27,17 @@ import ( type ServiceAddresses map[string][]int -func getServiceAddresses() ServiceAddresses { +func getServiceAddresses(config *config2.GlobalConfig) ServiceAddresses { return ServiceAddresses{ - config2.Config.RpcRegisterName.OpenImUserName: config2.Config.RpcPort.OpenImUserPort, - config2.Config.RpcRegisterName.OpenImFriendName: config2.Config.RpcPort.OpenImFriendPort, - config2.Config.RpcRegisterName.OpenImMsgName: config2.Config.RpcPort.OpenImMessagePort, - config2.Config.RpcRegisterName.OpenImMessageGatewayName: config2.Config.LongConnSvr.OpenImMessageGatewayPort, - config2.Config.RpcRegisterName.OpenImGroupName: config2.Config.RpcPort.OpenImGroupPort, - config2.Config.RpcRegisterName.OpenImAuthName: config2.Config.RpcPort.OpenImAuthPort, - config2.Config.RpcRegisterName.OpenImPushName: config2.Config.RpcPort.OpenImPushPort, - config2.Config.RpcRegisterName.OpenImConversationName: config2.Config.RpcPort.OpenImConversationPort, - config2.Config.RpcRegisterName.OpenImThirdName: config2.Config.RpcPort.OpenImThirdPort, + config.RpcRegisterName.OpenImUserName: config.RpcPort.OpenImUserPort, + config.RpcRegisterName.OpenImFriendName: config.RpcPort.OpenImFriendPort, + config.RpcRegisterName.OpenImMsgName: config.RpcPort.OpenImMessagePort, + config.RpcRegisterName.OpenImMessageGatewayName: config.LongConnSvr.OpenImMessageGatewayPort, + config.RpcRegisterName.OpenImGroupName: config.RpcPort.OpenImGroupPort, + config.RpcRegisterName.OpenImAuthName: config.RpcPort.OpenImAuthPort, + config.RpcRegisterName.OpenImPushName: config.RpcPort.OpenImPushPort, + config.RpcRegisterName.OpenImConversationName: config.RpcPort.OpenImConversationPort, + config.RpcRegisterName.OpenImThirdName: config.RpcPort.OpenImThirdPort, } } @@ -46,6 +46,7 @@ type ConnDirect struct { currentServiceAddress string conns map[string][]*grpc.ClientConn resolverDirect *ResolverDirect + config *config2.GlobalConfig } func (cd *ConnDirect) GetClientLocalConns() map[string][]*grpc.ClientConn { @@ -80,10 +81,11 @@ func (cd *ConnDirect) Close() { } -func NewConnDirect() (*ConnDirect, error) { +func NewConnDirect(config *config2.GlobalConfig) (*ConnDirect, error) { return &ConnDirect{ conns: make(map[string][]*grpc.ClientConn), resolverDirect: NewResolverDirect(), + config: config, }, nil } @@ -93,12 +95,12 @@ func (cd *ConnDirect) GetConns(ctx context.Context, if conns, exists := cd.conns[serviceName]; exists { return conns, nil } - ports := getServiceAddresses()[serviceName] + ports := getServiceAddresses(cd.config)[serviceName] var connections []*grpc.ClientConn for _, port := range ports { - conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...) + conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...) if err != nil { - fmt.Printf("connect to port %d failed,serviceName %s, IP %s\n", port, serviceName, config2.Config.Rpc.ListenIP) + fmt.Printf("connect to port %d failed,serviceName %s, IP %s\n", port, serviceName, cd.config.Rpc.ListenIP) } connections = append(connections, conn) } @@ -111,7 +113,7 @@ func (cd *ConnDirect) GetConns(ctx context.Context, func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) { // Get service addresses - addresses := getServiceAddresses() + addresses := getServiceAddresses(cd.config) address, ok := addresses[serviceName] if !ok { return nil, errs.Wrap(errors.New("unknown service name"), "serviceName", serviceName) @@ -119,9 +121,9 @@ func (cd *ConnDirect) GetConn(ctx context.Context, serviceName string, opts ...g var result string for _, addr := range address { if result != "" { - result = result + "," + fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", addr) + result = result + "," + fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr) } else { - result = fmt.Sprintf(config2.Config.Rpc.ListenIP+":%d", addr) + result = fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", addr) } } // Try to dial a new connection diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go index a21d8d62a..c43583a80 100644 --- a/pkg/common/discoveryregister/discoveryregister.go +++ b/pkg/common/discoveryregister/discoveryregister.go @@ -16,6 +16,7 @@ package discoveryregister import ( "errors" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "os" "github.com/OpenIMSDK/tools/discoveryregistry" @@ -26,19 +27,19 @@ import ( ) // NewDiscoveryRegister creates a new service discovery and registry client based on the provided environment type. -func NewDiscoveryRegister(envType string) (discoveryregistry.SvcDiscoveryRegistry, error) { +func NewDiscoveryRegister(config *config.GlobalConfig) (discoveryregistry.SvcDiscoveryRegistry, error) { if os.Getenv("ENVS_DISCOVERY") != "" { - envType = os.Getenv("ENVS_DISCOVERY") + config.Envs.Discovery = os.Getenv("ENVS_DISCOVERY") } - switch envType { + switch config.Envs.Discovery { case "zookeeper": - return zookeeper.NewZookeeperDiscoveryRegister() + return zookeeper.NewZookeeperDiscoveryRegister(config) case "k8s": - return kubernetes.NewK8sDiscoveryRegister() + return kubernetes.NewK8sDiscoveryRegister(config.RpcRegisterName.OpenImMessageGatewayName) case "direct": - return direct.NewConnDirect() + return direct.NewConnDirect(config) default: return nil, errs.Wrap(errors.New("envType not correct")) } diff --git a/pkg/common/discoveryregister/discoveryregister_test.go b/pkg/common/discoveryregister/discoveryregister_test.go index e7a5fb276..7d4fa53cd 100644 --- a/pkg/common/discoveryregister/discoveryregister_test.go +++ b/pkg/common/discoveryregister/discoveryregister_test.go @@ -15,6 +15,7 @@ package discoveryregister import ( + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "os" "testing" @@ -32,20 +33,23 @@ func setupTestEnvironment() { func TestNewDiscoveryRegister(t *testing.T) { setupTestEnvironment() - + conf := config.NewGlobalConfig() tests := []struct { envType string + gatewayName string expectedError bool expectedResult bool }{ - {"zookeeper", false, true}, - {"k8s", false, true}, // Assume that the k8s configuration is also set up correctly - {"direct", false, true}, - {"invalid", true, false}, + {"zookeeper", "MessageGateway", false, true}, + {"k8s", "MessageGateway", false, true}, + {"direct", "MessageGateway", false, true}, + {"invalid", "MessageGateway", true, false}, } for _, test := range tests { - client, err := NewDiscoveryRegister(test.envType) + conf.Envs.Discovery = test.envType + conf.RpcRegisterName.OpenImMessageGatewayName = test.gatewayName + client, err := NewDiscoveryRegister(conf) if test.expectedError { assert.Error(t, err) diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index 1292c64a8..b5d603fd1 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -22,11 +22,12 @@ import ( "strconv" "strings" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/stathat/consistent" + "google.golang.org/grpc" + + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/log" ) // K8sDR represents the Kubernetes service discovery and registration client. @@ -34,11 +35,12 @@ type K8sDR struct { options []grpc.DialOption rpcRegisterAddr string gatewayHostConsistent *consistent.Consistent + gatewayName string } -func NewK8sDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, error) { +func NewK8sDiscoveryRegister(gatewayName string) (discoveryregistry.SvcDiscoveryRegistry, error) { gatewayConsistent := consistent.New() - gatewayHosts := getMsgGatewayHost(context.Background()) + gatewayHosts := getMsgGatewayHost(context.Background(), gatewayName) for _, v := range gatewayHosts { gatewayConsistent.Add(v) } @@ -46,10 +48,10 @@ func NewK8sDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, error) { } func (cli *K8sDR) Register(serviceName, host string, port int, opts ...grpc.DialOption) error { - if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { + if serviceName != cli.gatewayName { cli.rpcRegisterAddr = serviceName } else { - cli.rpcRegisterAddr = getSelfHost(context.Background()) + cli.rpcRegisterAddr = getSelfHost(context.Background(), cli.gatewayName) } return nil @@ -81,15 +83,15 @@ func (cli *K8sDR) GetUserIdHashGatewayHost(ctx context.Context, userId string) ( } return host, err } -func getSelfHost(ctx context.Context) string { +func getSelfHost(ctx context.Context, gatewayName string) string { port := 88 instance := "openimserver" selfPodName := os.Getenv("MY_POD_NAME") ns := os.Getenv("MY_POD_NAMESPACE") statefuleIndex := 0 - gatewayEnds := strings.Split(config.Config.RpcRegisterName.OpenImMessageGatewayName, ":") + gatewayEnds := strings.Split(gatewayName, ":") if len(gatewayEnds) != 2 { - log.ZError(ctx, "msggateway RpcRegisterName is error:config.Config.RpcRegisterName.OpenImMessageGatewayName", errors.New("config error")) + log.ZError(ctx, "msggateway RpcRegisterName is error:config.RpcRegisterName.OpenImMessageGatewayName", errors.New("config error")) } else { port, _ = strconv.Atoi(gatewayEnds[1]) } @@ -102,15 +104,15 @@ func getSelfHost(ctx context.Context) string { } // like openimserver-openim-msggateway-0.openimserver-openim-msggateway-headless.openim-lin.svc.cluster.local:88. -func getMsgGatewayHost(ctx context.Context) []string { +func getMsgGatewayHost(ctx context.Context, gatewayName string) []string { port := 88 instance := "openimserver" selfPodName := os.Getenv("MY_POD_NAME") replicas := os.Getenv("MY_MSGGATEWAY_REPLICACOUNT") ns := os.Getenv("MY_POD_NAMESPACE") - gatewayEnds := strings.Split(config.Config.RpcRegisterName.OpenImMessageGatewayName, ":") + gatewayEnds := strings.Split(gatewayName, ":") if len(gatewayEnds) != 2 { - log.ZError(ctx, "msggateway RpcRegisterName is error:config.Config.RpcRegisterName.OpenImMessageGatewayName", errors.New("config error")) + log.ZError(ctx, "msggateway RpcRegisterName is error:config.RpcRegisterName.OpenImMessageGatewayName", errors.New("config error")) } else { port, _ = strconv.Atoi(gatewayEnds[1]) } @@ -131,7 +133,7 @@ func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc // This conditional checks if the serviceName is not the OpenImMessageGatewayName. // It seems to handle a special case for the OpenImMessageGateway. - if serviceName != config.Config.RpcRegisterName.OpenImMessageGatewayName { + if serviceName != cli.gatewayName { // DialContext creates a client connection to the given target (serviceName) using the specified context. // 'cli.options' are likely default or common options for all connections in this struct. // 'opts...' allows for additional gRPC dial options to be passed and used. @@ -146,7 +148,7 @@ func (cli *K8sDR) GetConns(ctx context.Context, serviceName string, opts ...grpc // getMsgGatewayHost presumably retrieves hosts for the message gateway service. // The context is passed, likely for cancellation and timeout control. - gatewayHosts := getMsgGatewayHost(ctx) + gatewayHosts := getMsgGatewayHost(ctx, cli.gatewayName) // Iterating over the retrieved gateway hosts. for _, host := range gatewayHosts { diff --git a/pkg/common/discoveryregister/zookeeper/zookeeper.go b/pkg/common/discoveryregister/zookeeper/zookeeper.go index 5f9a3b6bd..0aa40a907 100644 --- a/pkg/common/discoveryregister/zookeeper/zookeeper.go +++ b/pkg/common/discoveryregister/zookeeper/zookeeper.go @@ -28,11 +28,11 @@ import ( ) // NewZookeeperDiscoveryRegister creates a new instance of ZookeeperDR for Zookeeper service discovery and registration. -func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, error) { - schema := getEnv("ZOOKEEPER_SCHEMA", config.Config.Zookeeper.Schema) - zkAddr := getZkAddrFromEnv(config.Config.Zookeeper.ZkAddr) - username := getEnv("ZOOKEEPER_USERNAME", config.Config.Zookeeper.Username) - password := getEnv("ZOOKEEPER_PASSWORD", config.Config.Zookeeper.Password) +func NewZookeeperDiscoveryRegister(config *config.GlobalConfig) (discoveryregistry.SvcDiscoveryRegistry, error) { + schema := getEnv("ZOOKEEPER_SCHEMA", config.Zookeeper.Schema) + zkAddr := getZkAddrFromEnv(config.Zookeeper.ZkAddr) + username := getEnv("ZOOKEEPER_USERNAME", config.Zookeeper.Username) + password := getEnv("ZOOKEEPER_PASSWORD", config.Zookeeper.Password) zk, err := openkeeper.NewClient( zkAddr, @@ -46,10 +46,10 @@ func NewZookeeperDiscoveryRegister() (discoveryregistry.SvcDiscoveryRegistry, er if err != nil { uriFormat := "address:%s, username:%s, password:%s, schema:%s." errInfo := fmt.Sprintf(uriFormat, - config.Config.Zookeeper.ZkAddr, - config.Config.Zookeeper.Username, - config.Config.Zookeeper.Password, - config.Config.Zookeeper.Schema) + config.Zookeeper.ZkAddr, + config.Zookeeper.Username, + config.Zookeeper.Password, + config.Zookeeper.Schema) return nil, errs.Wrap(err, errInfo) } return zk, nil diff --git a/pkg/common/kafka/consumer.go b/pkg/common/kafka/consumer.go index d0e06d482..664e5d468 100644 --- a/pkg/common/kafka/consumer.go +++ b/pkg/common/kafka/consumer.go @@ -17,6 +17,8 @@ package kafka import ( "sync" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" ) @@ -29,22 +31,31 @@ type Consumer struct { Consumer sarama.Consumer } -func NewKafkaConsumer(addr []string, topic string, kafkaConfig *sarama.Config) (*Consumer, error) { - p := Consumer{ - Topic: topic, - addr: addr, +func NewKafkaConsumer(addr []string, topic string, config *config.GlobalConfig) (*Consumer,error) { + p := Consumer{} + p.Topic = topic + p.addr = addr + consumerConfig := sarama.NewConfig() + if config.Kafka.Username != "" && config.Kafka.Password != "" { + consumerConfig.Net.SASL.Enable = true + consumerConfig.Net.SASL.User = config.Kafka.Username + consumerConfig.Net.SASL.Password = config.Kafka.Password } - - if kafkaConfig.Net.SASL.User != "" && kafkaConfig.Net.SASL.Password != "" { - kafkaConfig.Net.SASL.Enable = true + var tlsConfig *TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: false, + } } - - err := SetupTLSConfig(kafkaConfig) - if err != nil { - return nil, err + err:=SetupTLSConfig(consumerConfig, tlsConfig) + if err!=nil{ + return nil,err } - - consumer, err := sarama.NewConsumer(p.addr, kafkaConfig) + consumer, err := sarama.NewConsumer(p.addr, consumerConfig) if err != nil { return nil, errs.Wrap(err, "NewKafkaConsumer: creating consumer failed") } diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index 824c5be2e..b6b4435ab 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,12 +17,12 @@ package kafka import ( "context" "errors" - "strings" "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + + "strings" ) type MConsumerGroup struct { @@ -35,22 +35,25 @@ type MConsumerGroupConfig struct { KafkaVersion sarama.KafkaVersion OffsetsInitial int64 IsReturnErr bool + UserName string + Password string } -func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string) (*MConsumerGroup, error) { +func NewMConsumerGroup(consumerConfig *MConsumerGroupConfig, topics, addrs []string, groupID string, tlsConfig *TLSConfig) (*MConsumerGroup, error) { consumerGroupConfig := sarama.NewConfig() consumerGroupConfig.Version = consumerConfig.KafkaVersion consumerGroupConfig.Consumer.Offsets.Initial = consumerConfig.OffsetsInitial consumerGroupConfig.Consumer.Return.Errors = consumerConfig.IsReturnErr - if config.Config.Kafka.Username != "" && config.Config.Kafka.Password != "" { + if consumerConfig.UserName != "" && consumerConfig.Password != "" { consumerGroupConfig.Net.SASL.Enable = true - consumerGroupConfig.Net.SASL.User = config.Config.Kafka.Username - consumerGroupConfig.Net.SASL.Password = config.Config.Kafka.Password + consumerGroupConfig.Net.SASL.User = consumerConfig.UserName + consumerGroupConfig.Net.SASL.Password = consumerConfig.Password } - SetupTLSConfig(consumerGroupConfig) + + SetupTLSConfig(consumerGroupConfig, tlsConfig) consumerGroup, err := sarama.NewConsumerGroup(addrs, groupID, consumerGroupConfig) if err != nil { - return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, config.Config.Kafka.Username, config.Config.Kafka.Password) + return nil, errs.Wrap(err, strings.Join(topics, ","), strings.Join(addrs, ","), groupID, consumerConfig.UserName, consumerConfig.Password) } return &MConsumerGroup{ diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index 4c2378c59..afc53b35a 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -22,12 +22,12 @@ import ( "strings" "time" + "github.com/OpenIMSDK/tools/errs" + "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "google.golang.org/protobuf/proto" ) @@ -43,8 +43,15 @@ type Producer struct { producer sarama.SyncProducer } +type ProducerConfig struct { + ProducerAck string + CompressType string + Username string + Password string +} + // NewKafkaProducer initializes a new Kafka producer. -func NewKafkaProducer(addr []string, topic string) (*Producer, error) { +func NewKafkaProducer(addr []string, topic string, producerConfig *ProducerConfig, tlsConfig *TLSConfig) (*Producer, error) { p := Producer{ addr: addr, topic: topic, @@ -59,14 +66,14 @@ func NewKafkaProducer(addr []string, topic string) (*Producer, error) { p.config.Producer.Partitioner = sarama.NewHashPartitioner // Configure producer acknowledgement level - configureProducerAck(&p, config.Config.Kafka.ProducerAck) + configureProducerAck(&p, producerConfig.ProducerAck) // Configure message compression - configureCompression(&p, config.Config.Kafka.CompressType) + configureCompression(&p, producerConfig.CompressType) // Get Kafka configuration from environment variables or fallback to config file - kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", config.Config.Kafka.Username) - kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", config.Config.Kafka.Password) + kafkaUsername := getEnvOrConfig("KAFKA_USERNAME", producerConfig.Username) + kafkaPassword := getEnvOrConfig("KAFKA_PASSWORD", producerConfig.Password) kafkaAddr := getKafkaAddrFromEnv(addr) // Updated to use the new function // Configure SASL authentication if credentials are provided @@ -80,7 +87,7 @@ func NewKafkaProducer(addr []string, topic string) (*Producer, error) { p.addr = kafkaAddr // Set up TLS configuration (if required) - SetupTLSConfig(p.config) + SetupTLSConfig(p.config, tlsConfig) // Create the producer with retries var err error diff --git a/pkg/common/kafka/util.go b/pkg/common/kafka/util.go index a94397819..4e2a02714 100644 --- a/pkg/common/kafka/util.go +++ b/pkg/common/kafka/util.go @@ -20,19 +20,27 @@ import ( "strings" "github.com/IBM/sarama" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/tls" ) +type TLSConfig struct { + CACrt string + ClientCrt string + ClientKey string + ClientKeyPwd string + InsecureSkipVerify bool +} + // SetupTLSConfig set up the TLS config from config file. -func SetupTLSConfig(cfg *sarama.Config) error { - if config.Config.Kafka.TLS != nil { +func SetupTLSConfig(cfg *sarama.Config, tlsConfig *TLSConfig) error { + if tlsConfig != nil { cfg.Net.TLS.Enable = true tlsConfig, err := tls.NewTLSConfig( - config.Config.Kafka.TLS.ClientCrt, - config.Config.Kafka.TLS.ClientKey, - config.Config.Kafka.TLS.CACrt, - []byte(config.Config.Kafka.TLS.ClientKeyPwd), + tlsConfig.ClientCrt, + tlsConfig.ClientKey, + tlsConfig.CACrt, + []byte(tlsConfig.ClientKeyPwd), + tlsConfig.InsecureSkipVerify, ) if err != nil { return err diff --git a/pkg/common/prommetrics/prommetrics.go b/pkg/common/prommetrics/prommetrics.go index 52694168f..9089e7b5f 100644 --- a/pkg/common/prommetrics/prommetrics.go +++ b/pkg/common/prommetrics/prommetrics.go @@ -31,17 +31,17 @@ func NewGrpcPromObj(cusMetrics []prometheus.Collector) (*prometheus.Registry, *g return reg, grpcMetrics, nil } -func GetGrpcCusMetrics(registerName string) []prometheus.Collector { +func GetGrpcCusMetrics(registerName string, config *config2.GlobalConfig) []prometheus.Collector { switch registerName { - case config2.Config.RpcRegisterName.OpenImMessageGatewayName: + case config.RpcRegisterName.OpenImMessageGatewayName: return []prometheus.Collector{OnlineUserGauge} - case config2.Config.RpcRegisterName.OpenImMsgName: + case config.RpcRegisterName.OpenImMsgName: return []prometheus.Collector{SingleChatMsgProcessSuccessCounter, SingleChatMsgProcessFailedCounter, GroupChatMsgProcessSuccessCounter, GroupChatMsgProcessFailedCounter} case "Transfer": return []prometheus.Collector{MsgInsertRedisSuccessCounter, MsgInsertRedisFailedCounter, MsgInsertMongoSuccessCounter, MsgInsertMongoFailedCounter, SeqSetFailedCounter} - case config2.Config.RpcRegisterName.OpenImPushName: + case config.RpcRegisterName.OpenImPushName: return []prometheus.Collector{MsgOfflinePushFailedCounter} - case config2.Config.RpcRegisterName.OpenImAuthName: + case config.RpcRegisterName.OpenImAuthName: return []prometheus.Collector{UserLoginCounter} default: return nil diff --git a/pkg/common/prommetrics/prommetrics_test.go b/pkg/common/prommetrics/prommetrics_test.go index 1e48c63ba..eb6f3c771 100644 --- a/pkg/common/prommetrics/prommetrics_test.go +++ b/pkg/common/prommetrics/prommetrics_test.go @@ -58,17 +58,20 @@ func TestNewGrpcPromObj(t *testing.T) { } func TestGetGrpcCusMetrics(t *testing.T) { + conf := config2.NewGlobalConfig() + + config2.InitConfig(conf, "../../config") // Test various cases based on the switch statement in the GetGrpcCusMetrics function. testCases := []struct { name string expected int // The expected number of metrics for each case. }{ - {config2.Config.RpcRegisterName.OpenImMessageGatewayName, 1}, + {conf.RpcRegisterName.OpenImMessageGatewayName, 1}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - metrics := GetGrpcCusMetrics(tc.name) + metrics := GetGrpcCusMetrics(tc.name, conf) assert.Len(t, metrics, tc.expected) }) } diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 1576762e8..8af894ac0 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "net" "net/http" "os" @@ -27,19 +28,25 @@ import ( "syscall" "time" - "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - "github.com/OpenIMSDK/tools/mw" - "github.com/OpenIMSDK/tools/network" - grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" + "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" + + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + + grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + + kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" + + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/mw" + "github.com/OpenIMSDK/tools/network" ) // Start rpc server. @@ -47,37 +54,38 @@ func Start( rpcPort int, rpcRegisterName string, prometheusPort int, - rpcFn func(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, + config *config2.GlobalConfig, + rpcFn func(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error, options ...grpc.ServerOption, ) error { fmt.Printf("start %s server, port: %d, prometheusPort: %d, OpenIM version: %s\n", - rpcRegisterName, rpcPort, prometheusPort, config.Version) - rpcTcpAddr := net.JoinHostPort(network.GetListenIP(config.Config.Rpc.ListenIP), strconv.Itoa(rpcPort)) + rpcRegisterName, rpcPort, prometheusPort, config2.Version) + rpcTcpAddr := net.JoinHostPort(network.GetListenIP(config.Rpc.ListenIP), strconv.Itoa(rpcPort)) listener, err := net.Listen( "tcp", rpcTcpAddr, ) if err != nil { - return errs.Wrap(err, "rpc start err", rpcTcpAddr) + return errs.Wrap(err, "listen err", rpcTcpAddr) } defer listener.Close() - client, err := kdisc.NewDiscoveryRegister(config.Config.Envs.Discovery) + client, err := kdisc.NewDiscoveryRegister(config) if err != nil { return err } defer client.Close() client.AddOption(mw.GrpcClient(), grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(fmt.Sprintf(`{"LoadBalancingPolicy": "%s"}`, "round_robin"))) - registerIP, err := network.GetRpcRegisterIP(config.Config.Rpc.RegisterIP) + registerIP, err := network.GetRpcRegisterIP(config.Rpc.RegisterIP) if err != nil { return errs.Wrap(err) } var reg *prometheus.Registry var metric *grpcprometheus.ServerMetrics - if config.Config.Prometheus.Enable { - cusMetrics := prommetrics.GetGrpcCusMetrics(rpcRegisterName) + if config.Prometheus.Enable { + cusMetrics := prommetrics.GetGrpcCusMetrics(rpcRegisterName, config) reg, metric, _ = prommetrics.NewGrpcPromObj(cusMetrics) options = append(options, mw.GrpcServer(), grpc.StreamInterceptor(metric.StreamServerInterceptor()), grpc.UnaryInterceptor(metric.UnaryServerInterceptor())) @@ -91,7 +99,7 @@ func Start( once.Do(srv.GracefulStop) }() - err = rpcFn(client, srv) + err = rpcFn(config, client, srv) if err != nil { return err } @@ -111,7 +119,7 @@ func Start( httpServer *http.Server ) go func() { - if config.Config.Prometheus.Enable && prometheusPort != 0 { + if config.Prometheus.Enable && prometheusPort != 0 { metric.InitializeMetrics(srv) // Create a HTTP server for prometheus. httpServer = &http.Server{Handler: promhttp.HandlerFor(reg, promhttp.HandlerOpts{}), Addr: fmt.Sprintf("0.0.0.0:%d", prometheusPort)} diff --git a/pkg/common/startrpc/start_test.go b/pkg/common/startrpc/start_test.go index 481986e15..e5e37e221 100644 --- a/pkg/common/startrpc/start_test.go +++ b/pkg/common/startrpc/start_test.go @@ -16,6 +16,7 @@ package startrpc import ( "fmt" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "net" "testing" "time" @@ -25,7 +26,7 @@ import ( ) // mockRpcFn is a mock gRPC function for testing. -func mockRpcFn(client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { +func mockRpcFn(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { // Implement a mock gRPC service registration logic if needed return nil } @@ -40,7 +41,8 @@ func TestStart(t *testing.T) { doneChan := make(chan error, 1) go func() { - err := Start(testRpcPort, testRpcRegisterName, testPrometheusPort, mockRpcFn) + err := Start(testRpcPort, testRpcRegisterName, testPrometheusPort, + config.NewGlobalConfig(), mockRpcFn) doneChan <- err }() diff --git a/pkg/common/tls/tls.go b/pkg/common/tls/tls.go old mode 100644 new mode 100755 index a52f46df7..736913758 --- a/pkg/common/tls/tls.go +++ b/pkg/common/tls/tls.go @@ -22,7 +22,6 @@ import ( "os" "github.com/OpenIMSDK/tools/errs" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) // decryptPEM decrypts a PEM block using a password. @@ -50,15 +49,14 @@ func readEncryptablePEMBlock(path string, pwd []byte) ([]byte, error) { } // NewTLSConfig setup the TLS config from general config file. -func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte) (*tls.Config, error) { - var tlsConfig tls.Config +func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte, insecureSkipVerify bool) (*tls.Config,error) { + tlsConfig := tls.Config{} if clientCertFile != "" && clientKeyFile != "" { certPEMBlock, err := os.ReadFile(clientCertFile) if err != nil { return nil, errs.Wrap(err, "NewTLSConfig: failed to read client cert file") } - keyPEMBlock, err := readEncryptablePEMBlock(clientKeyFile, keyPwd) if err != nil { return nil, err @@ -84,7 +82,7 @@ func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byt tlsConfig.RootCAs = caCertPool } - tlsConfig.InsecureSkipVerify = config.Config.Kafka.TLS.InsecureSkipVerify + tlsConfig.InsecureSkipVerify = insecureSkipVerify return &tlsConfig, nil } diff --git a/pkg/rpcclient/auth.go b/pkg/rpcclient/auth.go index bfd4b1119..24597120f 100644 --- a/pkg/rpcclient/auth.go +++ b/pkg/rpcclient/auth.go @@ -24,17 +24,18 @@ import ( "google.golang.org/grpc" ) -func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry) *Auth { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImAuthName) +func NewAuth(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Auth { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImAuthName) if err != nil { util.ExitWithError(err) } client := auth.NewAuthClient(conn) - return &Auth{discov: discov, conn: conn, Client: client} + return &Auth{discov: discov, conn: conn, Client: client, Config: config} } type Auth struct { conn grpc.ClientConnInterface Client auth.AuthClient discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } diff --git a/pkg/rpcclient/conversation.go b/pkg/rpcclient/conversation.go index ee9818f8f..6981844b6 100644 --- a/pkg/rpcclient/conversation.go +++ b/pkg/rpcclient/conversation.go @@ -30,21 +30,22 @@ type Conversation struct { Client pbconversation.ConversationClient conn grpc.ClientConnInterface discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } -func NewConversation(discov discoveryregistry.SvcDiscoveryRegistry) *Conversation { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImConversationName) +func NewConversation(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Conversation { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImConversationName) if err != nil { util.ExitWithError(err) } client := pbconversation.NewConversationClient(conn) - return &Conversation{discov: discov, conn: conn, Client: client} + return &Conversation{discov: discov, conn: conn, Client: client, Config: config} } type ConversationRpcClient Conversation -func NewConversationRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) ConversationRpcClient { - return ConversationRpcClient(*NewConversation(discov)) +func NewConversationRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) ConversationRpcClient { + return ConversationRpcClient(*NewConversation(discov, config)) } func (c *ConversationRpcClient) GetSingleConversationRecvMsgOpt(ctx context.Context, userID, conversationID string) (int32, error) { diff --git a/pkg/rpcclient/friend.go b/pkg/rpcclient/friend.go index 520616564..e1f6ed076 100644 --- a/pkg/rpcclient/friend.go +++ b/pkg/rpcclient/friend.go @@ -29,21 +29,22 @@ type Friend struct { conn grpc.ClientConnInterface Client friend.FriendClient discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } -func NewFriend(discov discoveryregistry.SvcDiscoveryRegistry) *Friend { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImFriendName) +func NewFriend(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Friend { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImFriendName) if err != nil { util.ExitWithError(err) } client := friend.NewFriendClient(conn) - return &Friend{discov: discov, conn: conn, Client: client} + return &Friend{discov: discov, conn: conn, Client: client, Config: config} } type FriendRpcClient Friend -func NewFriendRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) FriendRpcClient { - return FriendRpcClient(*NewFriend(discov)) +func NewFriendRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) FriendRpcClient { + return FriendRpcClient(*NewFriend(discov, config)) } func (f *FriendRpcClient) GetFriendsInfo( diff --git a/pkg/rpcclient/group.go b/pkg/rpcclient/group.go index bc9a3c75c..773637858 100644 --- a/pkg/rpcclient/group.go +++ b/pkg/rpcclient/group.go @@ -33,21 +33,22 @@ type Group struct { conn grpc.ClientConnInterface Client group.GroupClient discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } -func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry) *Group { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImGroupName) +func NewGroup(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Group { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImGroupName) if err != nil { util.ExitWithError(err) } client := group.NewGroupClient(conn) - return &Group{discov: discov, conn: conn, Client: client} + return &Group{discov: discov, conn: conn, Client: client, Config: config} } type GroupRpcClient Group -func NewGroupRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) GroupRpcClient { - return GroupRpcClient(*NewGroup(discov)) +func NewGroupRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) GroupRpcClient { + return GroupRpcClient(*NewGroup(discov, config)) } func (g *GroupRpcClient) GetGroupInfos( diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index e4008ed20..3db39925e 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -17,6 +17,8 @@ package rpcclient import ( "context" "encoding/json" + "fmt" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" @@ -29,47 +31,47 @@ import ( "google.golang.org/protobuf/proto" ) -func newContentTypeConf() map[int32]config.NotificationConf { +func newContentTypeConf(conf *config.GlobalConfig) map[int32]config.NotificationConf { return map[int32]config.NotificationConf{ // group - constant.GroupCreatedNotification: config.Config.Notification.GroupCreated, - constant.GroupInfoSetNotification: config.Config.Notification.GroupInfoSet, - constant.JoinGroupApplicationNotification: config.Config.Notification.JoinGroupApplication, - constant.MemberQuitNotification: config.Config.Notification.MemberQuit, - constant.GroupApplicationAcceptedNotification: config.Config.Notification.GroupApplicationAccepted, - constant.GroupApplicationRejectedNotification: config.Config.Notification.GroupApplicationRejected, - constant.GroupOwnerTransferredNotification: config.Config.Notification.GroupOwnerTransferred, - constant.MemberKickedNotification: config.Config.Notification.MemberKicked, - constant.MemberInvitedNotification: config.Config.Notification.MemberInvited, - constant.MemberEnterNotification: config.Config.Notification.MemberEnter, - constant.GroupDismissedNotification: config.Config.Notification.GroupDismissed, - constant.GroupMutedNotification: config.Config.Notification.GroupMuted, - constant.GroupCancelMutedNotification: config.Config.Notification.GroupCancelMuted, - constant.GroupMemberMutedNotification: config.Config.Notification.GroupMemberMuted, - constant.GroupMemberCancelMutedNotification: config.Config.Notification.GroupMemberCancelMuted, - constant.GroupMemberInfoSetNotification: config.Config.Notification.GroupMemberInfoSet, - constant.GroupMemberSetToAdminNotification: config.Config.Notification.GroupMemberSetToAdmin, - constant.GroupMemberSetToOrdinaryUserNotification: config.Config.Notification.GroupMemberSetToOrdinary, - constant.GroupInfoSetAnnouncementNotification: config.Config.Notification.GroupInfoSetAnnouncement, - constant.GroupInfoSetNameNotification: config.Config.Notification.GroupInfoSetName, + constant.GroupCreatedNotification: conf.Notification.GroupCreated, + constant.GroupInfoSetNotification: conf.Notification.GroupInfoSet, + constant.JoinGroupApplicationNotification: conf.Notification.JoinGroupApplication, + constant.MemberQuitNotification: conf.Notification.MemberQuit, + constant.GroupApplicationAcceptedNotification: conf.Notification.GroupApplicationAccepted, + constant.GroupApplicationRejectedNotification: conf.Notification.GroupApplicationRejected, + constant.GroupOwnerTransferredNotification: conf.Notification.GroupOwnerTransferred, + constant.MemberKickedNotification: conf.Notification.MemberKicked, + constant.MemberInvitedNotification: conf.Notification.MemberInvited, + constant.MemberEnterNotification: conf.Notification.MemberEnter, + constant.GroupDismissedNotification: conf.Notification.GroupDismissed, + constant.GroupMutedNotification: conf.Notification.GroupMuted, + constant.GroupCancelMutedNotification: conf.Notification.GroupCancelMuted, + constant.GroupMemberMutedNotification: conf.Notification.GroupMemberMuted, + constant.GroupMemberCancelMutedNotification: conf.Notification.GroupMemberCancelMuted, + constant.GroupMemberInfoSetNotification: conf.Notification.GroupMemberInfoSet, + constant.GroupMemberSetToAdminNotification: conf.Notification.GroupMemberSetToAdmin, + constant.GroupMemberSetToOrdinaryUserNotification: conf.Notification.GroupMemberSetToOrdinary, + constant.GroupInfoSetAnnouncementNotification: conf.Notification.GroupInfoSetAnnouncement, + constant.GroupInfoSetNameNotification: conf.Notification.GroupInfoSetName, // user - constant.UserInfoUpdatedNotification: config.Config.Notification.UserInfoUpdated, - constant.UserStatusChangeNotification: config.Config.Notification.UserStatusChanged, + constant.UserInfoUpdatedNotification: conf.Notification.UserInfoUpdated, + constant.UserStatusChangeNotification: conf.Notification.UserStatusChanged, // friend - constant.FriendApplicationNotification: config.Config.Notification.FriendApplicationAdded, - constant.FriendApplicationApprovedNotification: config.Config.Notification.FriendApplicationApproved, - constant.FriendApplicationRejectedNotification: config.Config.Notification.FriendApplicationRejected, - constant.FriendAddedNotification: config.Config.Notification.FriendAdded, - constant.FriendDeletedNotification: config.Config.Notification.FriendDeleted, - constant.FriendRemarkSetNotification: config.Config.Notification.FriendRemarkSet, - constant.BlackAddedNotification: config.Config.Notification.BlackAdded, - constant.BlackDeletedNotification: config.Config.Notification.BlackDeleted, - constant.FriendInfoUpdatedNotification: config.Config.Notification.FriendInfoUpdated, - constant.FriendsInfoUpdateNotification: config.Config.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated + constant.FriendApplicationNotification: conf.Notification.FriendApplicationAdded, + constant.FriendApplicationApprovedNotification: conf.Notification.FriendApplicationApproved, + constant.FriendApplicationRejectedNotification: conf.Notification.FriendApplicationRejected, + constant.FriendAddedNotification: conf.Notification.FriendAdded, + constant.FriendDeletedNotification: conf.Notification.FriendDeleted, + constant.FriendRemarkSetNotification: conf.Notification.FriendRemarkSet, + constant.BlackAddedNotification: conf.Notification.BlackAdded, + constant.BlackDeletedNotification: conf.Notification.BlackDeleted, + constant.FriendInfoUpdatedNotification: conf.Notification.FriendInfoUpdated, + constant.FriendsInfoUpdateNotification: conf.Notification.FriendInfoUpdated, //use the same FriendInfoUpdated // conversation - constant.ConversationChangeNotification: config.Config.Notification.ConversationChanged, - constant.ConversationUnreadNotification: config.Config.Notification.ConversationChanged, - constant.ConversationPrivateChatNotification: config.Config.Notification.ConversationSetPrivate, + constant.ConversationChangeNotification: conf.Notification.ConversationChanged, + constant.ConversationUnreadNotification: conf.Notification.ConversationChanged, + constant.ConversationPrivateChatNotification: conf.Notification.ConversationSetPrivate, // msg constant.MsgRevokeNotification: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg}, constant.HasReadReceipt: {IsSendMsg: false, ReliabilityLevel: constant.ReliableNotificationNoMsg}, @@ -127,21 +129,22 @@ type Message struct { conn grpc.ClientConnInterface Client msg.MsgClient discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } -func NewMessage(discov discoveryregistry.SvcDiscoveryRegistry) *Message { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImMsgName) +func NewMessage(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Message { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImMsgName) if err != nil { panic(err) } client := msg.NewMsgClient(conn) - return &Message{discov: discov, conn: conn, Client: client} + return &Message{discov: discov, conn: conn, Client: client, Config: config} } type MessageRpcClient Message -func NewMessageRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) MessageRpcClient { - return MessageRpcClient(*NewMessage(discov)) +func NewMessageRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) MessageRpcClient { + return MessageRpcClient(*NewMessage(discov, config)) } // SendMsg sends a message through the gRPC client and returns the response. @@ -234,8 +237,8 @@ func WithUserRpcClient(userRpcClient *UserRpcClient) NotificationSenderOptions { } } -func NewNotificationSender(opts ...NotificationSenderOptions) *NotificationSender { - notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(), sessionTypeConf: newSessionTypeConf()} +func NewNotificationSender(config *config.GlobalConfig, opts ...NotificationSenderOptions) *NotificationSender { + notificationSender := &NotificationSender{contentTypeConf: newContentTypeConf(config), sessionTypeConf: newSessionTypeConf()} for _, opt := range opts { opt(notificationSender) } @@ -258,8 +261,8 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s n := sdkws.NotificationElem{Detail: utils.StructToJsonString(m)} content, err := json.Marshal(&n) if err != nil { - log.ZError(ctx, "MsgClient Notification json.Marshal failed", err, "sendID", sendID, "recvID", recvID, "contentType", contentType, "msg", m) - return err + errInfo := fmt.Sprintf("MsgClient Notification json.Marshal failed, sendID:%s, recvID:%s, contentType:%d, msg:%s", sendID, recvID, contentType, m) + return errs.Wrap(err, errInfo) } notificationOpt := ¬ificationOpt{} for _, opt := range opts { @@ -271,7 +274,8 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s if notificationOpt.WithRpcGetUsername && s.getUserInfo != nil { userInfo, err = s.getUserInfo(ctx, sendID) if err != nil { - log.ZWarn(ctx, "getUserInfo failed", err, "sendID", sendID) + errInfo := fmt.Sprintf("getUserInfo failed, sendID:%s", sendID) + return errs.Wrap(err, errInfo) } else { msg.SenderNickname = userInfo.Nickname msg.SenderFaceURL = userInfo.FaceURL @@ -303,10 +307,9 @@ func (s *NotificationSender) NotificationWithSesstionType(ctx context.Context, s msg.OfflinePushInfo = &offlineInfo req.MsgData = &msg _, err = s.sendMsg(ctx, &req) - if err == nil { - log.ZDebug(ctx, "MsgClient Notification SendMsg success", "req", &req) - } else { - log.ZError(ctx, "MsgClient Notification SendMsg failed", err, "req", &req) + if err != nil { + errInfo := fmt.Sprintf("MsgClient Notification SendMsg failed, req:%s", &req) + return errs.Wrap(err, errInfo) } return err } diff --git a/pkg/rpcclient/notification/conversation.go b/pkg/rpcclient/notification/conversation.go index b43e5494a..115fb81a2 100644 --- a/pkg/rpcclient/notification/conversation.go +++ b/pkg/rpcclient/notification/conversation.go @@ -16,6 +16,7 @@ package notification import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" @@ -26,8 +27,8 @@ type ConversationNotificationSender struct { *rpcclient.NotificationSender } -func NewConversationNotificationSender(msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender { - return &ConversationNotificationSender{rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient))} +func NewConversationNotificationSender(config *config.GlobalConfig, msgRpcClient *rpcclient.MessageRpcClient) *ConversationNotificationSender { + return &ConversationNotificationSender{rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient))} } // SetPrivate invote. diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index 9237111be..31426da31 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -16,6 +16,7 @@ package notification import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" @@ -80,11 +81,12 @@ func WithRpcFunc( } func NewFriendNotificationSender( + config *config.GlobalConfig, msgRpcClient *rpcclient.MessageRpcClient, opts ...friendNotificationSenderOptions, ) *FriendNotificationSender { f := &FriendNotificationSender{ - NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)), + NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient)), } for _, opt := range opts { opt(f) diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 1778a498d..5500f4f43 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -17,6 +17,7 @@ package notification import ( "context" "fmt" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbgroup "github.com/OpenIMSDK/protocol/group" @@ -35,12 +36,14 @@ func NewGroupNotificationSender( db controller.GroupDatabase, msgRpcClient *rpcclient.MessageRpcClient, userRpcClient *rpcclient.UserRpcClient, + config *config.GlobalConfig, fn func(ctx context.Context, userIDs []string) ([]CommonUser, error), ) *GroupNotificationSender { return &GroupNotificationSender{ - NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)), + NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient), rpcclient.WithUserRpcClient(userRpcClient)), getUsersInfo: fn, db: db, + config: config, } } @@ -48,6 +51,7 @@ type GroupNotificationSender struct { *rpcclient.NotificationSender getUsersInfo func(ctx context.Context, userIDs []string) ([]CommonUser, error) db controller.GroupDatabase + config *config.GlobalConfig } func (g *GroupNotificationSender) PopulateGroupMember(ctx context.Context, members ...*relation.GroupMemberModel) error { @@ -243,21 +247,15 @@ func (g *GroupNotificationSender) groupMemberDB2PB(member *relation.GroupMemberM } */ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws.GroupMemberFullInfo, groupID string) (err error) { - defer log.ZDebug(ctx, "return") - defer func() { - if err != nil { - log.ZError(ctx, utils.GetFuncName(1)+" failed", err) - } - }() if opUser == nil { return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil") } if *opUser != nil { - return nil + return errs.ErrArgs.Wrap("*opUser is not nil") } userID := mcontext.GetOpUserID(ctx) if groupID != "" { - if authverify.IsManagerUserID(userID) { + if authverify.IsManagerUserID(userID, g.config) { *opUser = &sdkws.GroupMemberFullInfo{ GroupID: groupID, UserID: userID, @@ -265,11 +263,11 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws AppMangerLevel: constant.AppAdmin, } } else { - member, err2 := g.db.TakeGroupMember(ctx, groupID, userID) - if err2 == nil { + member, err := g.db.TakeGroupMember(ctx, groupID, userID) + if err == nil { *opUser = g.groupMemberDB2PB(member, 0) - } else if !errs.ErrRecordNotFound.Is(err2) { - return err2 + } else if !errs.ErrRecordNotFound.Is(err) { + return err } } } @@ -650,12 +648,6 @@ func (g *GroupNotificationSender) GroupCancelMutedNotification(ctx context.Conte } func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { - defer log.ZDebug(ctx, "return") - defer func() { - if err != nil { - log.ZError(ctx, utils.GetFuncName(1)+" failed", err) - } - }() group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err @@ -672,12 +664,6 @@ func (g *GroupNotificationSender) GroupMemberInfoSetNotification(ctx context.Con } func (g *GroupNotificationSender) GroupMemberSetToAdminNotification(ctx context.Context, groupID, groupMemberUserID string) (err error) { - defer log.ZDebug(ctx, "return") - defer func() { - if err != nil { - log.ZError(ctx, utils.GetFuncName(1)+" failed", err) - } - }() group, err := g.getGroupInfo(ctx, groupID) if err != nil { return err diff --git a/pkg/rpcclient/notification/msg.go b/pkg/rpcclient/notification/msg.go index 83280f80a..19819e7b7 100644 --- a/pkg/rpcclient/notification/msg.go +++ b/pkg/rpcclient/notification/msg.go @@ -16,6 +16,7 @@ package notification import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" @@ -26,8 +27,8 @@ type MsgNotificationSender struct { *rpcclient.NotificationSender } -func NewMsgNotificationSender(opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender { - return &MsgNotificationSender{rpcclient.NewNotificationSender(opts...)} +func NewMsgNotificationSender(config *config.GlobalConfig, opts ...rpcclient.NotificationSenderOptions) *MsgNotificationSender { + return &MsgNotificationSender{rpcclient.NewNotificationSender(config, opts...)} } func (m *MsgNotificationSender) UserDeleteMsgsNotification(ctx context.Context, userID, conversationID string, seqs []int64) error { diff --git a/pkg/rpcclient/notification/user.go b/pkg/rpcclient/notification/user.go index f94e59a33..204b13e61 100644 --- a/pkg/rpcclient/notification/user.go +++ b/pkg/rpcclient/notification/user.go @@ -16,6 +16,7 @@ package notification import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" @@ -58,11 +59,12 @@ func WithUserFunc( } func NewUserNotificationSender( + config *config.GlobalConfig, msgRpcClient *rpcclient.MessageRpcClient, opts ...userNotificationSenderOptions, ) *UserNotificationSender { f := &UserNotificationSender{ - NotificationSender: rpcclient.NewNotificationSender(rpcclient.WithRpcClient(msgRpcClient)), + NotificationSender: rpcclient.NewNotificationSender(config, rpcclient.WithRpcClient(msgRpcClient)), } for _, opt := range opts { opt(f) diff --git a/pkg/rpcclient/push.go b/pkg/rpcclient/push.go index 2f540da81..c0aa9efa4 100644 --- a/pkg/rpcclient/push.go +++ b/pkg/rpcclient/push.go @@ -30,8 +30,8 @@ type Push struct { discov discoveryregistry.SvcDiscoveryRegistry } -func NewPush(discov discoveryregistry.SvcDiscoveryRegistry) *Push { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImPushName) +func NewPush(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Push { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImPushName) if err != nil { util.ExitWithError(err) } @@ -44,8 +44,8 @@ func NewPush(discov discoveryregistry.SvcDiscoveryRegistry) *Push { type PushRpcClient Push -func NewPushRpcClient(discov discoveryregistry.SvcDiscoveryRegistry) PushRpcClient { - return PushRpcClient(*NewPush(discov)) +func NewPushRpcClient(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) PushRpcClient { + return PushRpcClient(*NewPush(discov, config)) } func (p *PushRpcClient) DelUserPushToken(ctx context.Context, req *push.DelUserPushTokenReq) (*push.DelUserPushTokenResp, error) { diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go old mode 100644 new mode 100755 index be40335d5..50d545a64 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -16,13 +16,15 @@ package rpcclient import ( "context" + "github.com/OpenIMSDK/tools/errs" "net/url" - "github.com/OpenIMSDK/protocol/third" - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/errs" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" + + "github.com/OpenIMSDK/protocol/third" + "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "google.golang.org/grpc" @@ -33,47 +35,41 @@ type Third struct { Client third.ThirdClient discov discoveryregistry.SvcDiscoveryRegistry MinioClient *minio.Client + Config *config.GlobalConfig } -func NewThird(discov discoveryregistry.SvcDiscoveryRegistry) *Third { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImThirdName) +func NewThird(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Third { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImThirdName) if err != nil { util.ExitWithError(err) } client := third.NewThirdClient(conn) - minioClient, err := minioInit() + minioClient, err := minioInit(config) if err != nil { util.ExitWithError(err) } - return &Third{discov: discov, Client: client, conn: conn, MinioClient: minioClient} + return &Third{discov: discov, Client: client, conn: conn, MinioClient: minioClient, Config: config} } -func minioInit() (*minio.Client, error) { - // Retrieve MinIO configuration details - endpoint := config.Config.Object.Minio.Endpoint - accessKeyID := config.Config.Object.Minio.AccessKeyID - secretAccessKey := config.Config.Object.Minio.SecretAccessKey - - // Parse the MinIO URL to determine if the connection should be secure - minioURL, err := url.Parse(endpoint) +func minioInit(config *config.GlobalConfig) (*minio.Client, error) { + minioClient := &minio.Client{} + initUrl := config.Object.Minio.Endpoint + minioUrl, err := url.Parse(initUrl) if err != nil { return nil, errs.Wrap(err, "minioInit: failed to parse MinIO endpoint URL") } - - // Determine the security of the connection based on the scheme - secure := minioURL.Scheme == "https" - - // Setup MinIO client options opts := &minio.Options{ - Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), - Secure: secure, + Creds: credentials.NewStaticV4(config.Object.Minio.AccessKeyID, config.Object.Minio.SecretAccessKey, ""), + // Region: config.Credential.Minio.Location, } - - // Initialize MinIO client - minioClient, err := minio.New(minioURL.Host, opts) + if minioUrl.Scheme == "http" { + opts.Secure = false + } else if minioUrl.Scheme == "https" { + opts.Secure = true + } + minioClient, err = minio.New(minioUrl.Host, opts) if err != nil { return nil, errs.Wrap(err, "minioInit: failed to create MinIO client") } - return minioClient, nil } diff --git a/pkg/rpcclient/user.go b/pkg/rpcclient/user.go index a6c202129..08ad41dc1 100644 --- a/pkg/rpcclient/user.go +++ b/pkg/rpcclient/user.go @@ -34,16 +34,17 @@ type User struct { conn grpc.ClientConnInterface Client user.UserClient Discov discoveryregistry.SvcDiscoveryRegistry + Config *config.GlobalConfig } // NewUser initializes and returns a User instance based on the provided service discovery registry. -func NewUser(discov discoveryregistry.SvcDiscoveryRegistry) *User { - conn, err := discov.GetConn(context.Background(), config.Config.RpcRegisterName.OpenImUserName) +func NewUser(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *User { + conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImUserName) if err != nil { util.ExitWithError(err) } client := user.NewUserClient(conn) - return &User{Discov: discov, Client: client, conn: conn} + return &User{Discov: discov, Client: client, conn: conn, Config: config} } // UserRpcClient represents the structure for a User RPC client. @@ -56,8 +57,8 @@ func NewUserRpcClientByUser(user *User) *UserRpcClient { } // NewUserRpcClient initializes a UserRpcClient based on the provided service discovery registry. -func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry) UserRpcClient { - return UserRpcClient(*NewUser(client)) +func NewUserRpcClient(client discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) UserRpcClient { + return UserRpcClient(*NewUser(client, config)) } // GetUsersInfo retrieves information for multiple users based on their user IDs. @@ -160,7 +161,7 @@ func (u *UserRpcClient) Access(ctx context.Context, ownerUserID string) error { if err != nil { return err } - return authverify.CheckAccessV3(ctx, ownerUserID) + return authverify.CheckAccessV3(ctx, ownerUserID, u.Config) } // GetAllUserIDs retrieves all user IDs with pagination options. diff --git a/tools/component/component.go b/tools/component/component.go index 6b879d7f8..bb3a2d46f 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -47,27 +47,33 @@ var ( cfgPath = flag.String("c", defaultCfgPath, "Path to the configuration file") ) -func initCfg() error { +func initCfg() (*config.GlobalConfig, error) { data, err := os.ReadFile(*cfgPath) if err != nil { - return err + return nil, errs.Wrap(err, "ReadFile unmarshal failed") } - return yaml.Unmarshal(data, &config.Config) + conf := config.NewGlobalConfig() + err = yaml.Unmarshal(data, &conf) + if err != nil { + return nil, errs.Wrap(err, "InitConfig unmarshal failed") + } + return conf, nil } type checkFunc struct { name string - function func() error + function func(*config.GlobalConfig) error flag bool + config *config.GlobalConfig } func main() { flag.Parse() - if err := initCfg(); err != nil { + conf, err := initCfg() + if err != nil { fmt.Printf("Read config failed: %v\n", err) - return } @@ -75,11 +81,11 @@ func main() { checks := []checkFunc{ //{name: "Mysql", function: checkMysql}, - {name: "Mongo", function: checkMongo}, - {name: "Redis", function: checkRedis}, - {name: "Minio", function: checkMinio}, - {name: "Zookeeper", function: checkZookeeper}, - {name: "Kafka", function: checkKafka}, + {name: "Mongo", function: checkMongo, config: conf}, + {name: "Redis", function: checkRedis, config: conf}, + {name: "Minio", function: checkMinio, config: conf}, + {name: "Zookeeper", function: checkZookeeper, config: conf}, + {name: "Kafka", function: checkKafka, config: conf}, } for i := 0; i < maxRetry; i++ { @@ -92,7 +98,7 @@ func main() { allSuccess := true for index, check := range checks { if !check.flag { - err = check.function() + err = check.function(check.config) if err != nil { component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err)) allSuccess = false @@ -112,30 +118,30 @@ func main() { } // checkMongo checks the MongoDB connection without retries -func checkMongo() error { - _, err := unrelation.NewMongo() +func checkMongo(config *config.GlobalConfig) error { + _, err := unrelation.NewMongo(config) return err } // checkRedis checks the Redis connection -func checkRedis() error { - _, err := cache.NewRedis() +func checkRedis(config *config.GlobalConfig) error { + _, err := cache.NewRedis(config) return err } // checkMinio checks the MinIO connection -func checkMinio() error { +func checkMinio(config *config.GlobalConfig) error { // Check if MinIO is enabled - if config.Config.Object.Enable != "minio" { + if config.Object.Enable != "minio" { return errs.Wrap(errors.New("minio.Enable is empty")) } minio := &component.Minio{ - ApiURL: config.Config.Object.ApiURL, - Endpoint: config.Config.Object.Minio.Endpoint, - AccessKeyID: config.Config.Object.Minio.AccessKeyID, - SecretAccessKey: config.Config.Object.Minio.SecretAccessKey, - SignEndpoint: config.Config.Object.Minio.SignEndpoint, + ApiURL: config.Object.ApiURL, + Endpoint: config.Object.Minio.Endpoint, + AccessKeyID: config.Object.Minio.AccessKeyID, + SecretAccessKey: config.Object.Minio.SecretAccessKey, + SignEndpoint: config.Object.Minio.SignEndpoint, UseSSL: getEnv("MINIO_USE_SSL", "false"), } err := component.CheckMinio(minio) @@ -143,18 +149,18 @@ func checkMinio() error { } // checkZookeeper checks the Zookeeper connection -func checkZookeeper() error { - _, err := zookeeper.NewZookeeperDiscoveryRegister() +func checkZookeeper(config *config.GlobalConfig) error { + _, err := zookeeper.NewZookeeperDiscoveryRegister(config) return err } // checkKafka checks the Kafka connection -func checkKafka() error { +func checkKafka(config *config.GlobalConfig) error { // Prioritize environment variables kafkaStu := &component.Kafka{ - Username: config.Config.Kafka.Username, - Password: config.Config.Kafka.Password, - Addr: config.Config.Kafka.Addr, + Username: config.Kafka.Username, + Password: config.Kafka.Password, + Addr: config.Kafka.Addr, } kafkaClient, err := component.CheckKafka(kafkaStu) @@ -170,9 +176,9 @@ func checkKafka() error { } requiredTopics := []string{ - config.Config.Kafka.MsgToMongo.Topic, - config.Config.Kafka.MsgToPush.Topic, - config.Config.Kafka.LatestMsgToRedis.Topic, + config.Kafka.MsgToMongo.Topic, + config.Kafka.MsgToPush.Topic, + config.Kafka.LatestMsgToRedis.Topic, } for _, requiredTopic := range requiredTopics { @@ -181,11 +187,25 @@ func checkKafka() error { } } + var tlsConfig *kafka.TLSConfig + if config.Kafka.TLS != nil { + tlsConfig = &kafka.TLSConfig{ + CACrt: config.Kafka.TLS.CACrt, + ClientCrt: config.Kafka.TLS.ClientCrt, + ClientKey: config.Kafka.TLS.ClientKey, + ClientKeyPwd: config.Kafka.TLS.ClientKeyPwd, + InsecureSkipVerify: config.Kafka.TLS.InsecureSkipVerify, + } + } + _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, - OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.LatestMsgToRedis.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToRedis) + OffsetsInitial: sarama.OffsetNewest, + IsReturnErr: false, + UserName: config.Kafka.Username, + Password: config.Kafka.Password, + }, []string{config.Kafka.LatestMsgToRedis.Topic}, + config.Kafka.Addr, config.Kafka.ConsumerGroupID.MsgToRedis, tlsConfig) if err != nil { return err } @@ -193,8 +213,8 @@ func checkKafka() error { _, err = kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.MsgToPush.Topic}, - config.Config.Kafka.Addr, config.Config.Kafka.ConsumerGroupID.MsgToMongo) + }, []string{config.Kafka.MsgToPush.Topic}, + config.Kafka.Addr, config.Kafka.ConsumerGroupID.MsgToMongo, tlsConfig) if err != nil { return err } @@ -202,8 +222,8 @@ func checkKafka() error { kafka.NewMConsumerGroup(&kafka.MConsumerGroupConfig{ KafkaVersion: sarama.V2_0_0_0, OffsetsInitial: sarama.OffsetNewest, IsReturnErr: false, - }, []string{config.Config.Kafka.MsgToPush.Topic}, config.Config.Kafka.Addr, - config.Config.Kafka.ConsumerGroupID.MsgToPush) + }, []string{config.Kafka.MsgToPush.Topic}, config.Kafka.Addr, + config.Kafka.ConsumerGroupID.MsgToPush, tlsConfig) if err != nil { return err } diff --git a/tools/component/component_test.go b/tools/component/component_test.go index 4488c029e..c56361b2c 100644 --- a/tools/component/component_test.go +++ b/tools/component/component_test.go @@ -21,20 +21,11 @@ import ( "time" "github.com/redis/go-redis/v9" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) -// Mock for initCfg for testing purpose -func mockInitCfg() error { - config.Config.Mysql.Username = "root" - config.Config.Mysql.Password = "openIM123" - config.Config.Mysql.Address = []string{"127.0.0.1:13306"} - return nil -} - func TestRedis(t *testing.T) { - config.Config.Redis.Address = []string{ + conf, err := initCfg() + conf.Redis.Address = []string{ "172.16.8.142:7000", //"172.16.8.142:7000", "172.16.8.142:7001", "172.16.8.142:7002", "172.16.8.142:7003", "172.16.8.142:7004", "172.16.8.142:7005", } @@ -45,20 +36,20 @@ func TestRedis(t *testing.T) { redisClient.Close() } }() - if len(config.Config.Redis.Address) > 1 { + if len(conf.Redis.Address) > 1 { redisClient = redis.NewClusterClient(&redis.ClusterOptions{ - Addrs: config.Config.Redis.Address, - Username: config.Config.Redis.Username, - Password: config.Config.Redis.Password, + Addrs: conf.Redis.Address, + Username: conf.Redis.Username, + Password: conf.Redis.Password, }) } else { redisClient = redis.NewClient(&redis.Options{ - Addr: config.Config.Redis.Address[0], - Username: config.Config.Redis.Username, - Password: config.Config.Redis.Password, + Addr: conf.Redis.Address[0], + Username: conf.Redis.Username, + Password: conf.Redis.Password, }) } - _, err := redisClient.Ping(context.Background()).Result() + _, err = redisClient.Ping(context.Background()).Result() if err != nil { t.Fatal(err) } diff --git a/tools/up35/pkg/pkg.go b/tools/up35/pkg/pkg.go index b7e7c01f5..1348172d2 100644 --- a/tools/up35/pkg/pkg.go +++ b/tools/up35/pkg/pkg.go @@ -18,6 +18,7 @@ import ( "context" "errors" "fmt" + "github.com/OpenIMSDK/tools/errs" "log" "os" "reflect" @@ -45,36 +46,43 @@ const ( versionValue = 35 ) -func InitConfig(path string) error { +func InitConfig(path string) (*config.GlobalConfig, error) { data, err := os.ReadFile(path) if err != nil { - return err + return nil, errs.Wrap(err, "ReadFile unmarshal failed") } - return yaml.Unmarshal(data, &config.Config) + + conf := config.NewGlobalConfig() + err = yaml.Unmarshal(data, &conf) + if err != nil { + return nil, errs.Wrap(err, "InitConfig unmarshal failed") + } + return conf, nil } -func GetMysql() (*gorm.DB, error) { - conf := config.Config.Mysql +func GetMysql(config *config.GlobalConfig) (*gorm.DB, error) { + conf := config.Mysql mysqlDSN := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", conf.Username, conf.Password, conf.Address[0], conf.Database) return gorm.Open(gormmysql.Open(mysqlDSN), &gorm.Config{Logger: logger.Discard}) } -func GetMongo() (*mongo.Database, error) { - mgo, err := unrelation.NewMongo() +func GetMongo(config *config.GlobalConfig) (*mongo.Database, error) { + mgo, err := unrelation.NewMongo(config) if err != nil { return nil, err } - return mgo.GetDatabase(), nil + return mgo.GetDatabase(config.Mongo.Database), nil } func Main(path string) error { - if err := InitConfig(path); err != nil { + conf, err := InitConfig(path) + if err != nil { return err } - if config.Config.Mysql == nil { + if conf.Mysql == nil { return nil } - mongoDB, err := GetMongo() + mongoDB, err := GetMongo(conf) if err != nil { return err } @@ -91,7 +99,7 @@ func Main(path string) error { default: return err } - mysqlDB, err := GetMysql() + mysqlDB, err := GetMysql(conf) if err != nil { if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1049 { if err := SetMongoDataVersion(mongoDB, version.Value); err != nil { @@ -113,7 +121,7 @@ func Main(path string) error { func() error { return NewTask(mysqlDB, mongoDB, mgo.NewGroupMember, c.GroupMember) }, func() error { return NewTask(mysqlDB, mongoDB, mgo.NewGroupRequestMgo, c.GroupRequest) }, func() error { return NewTask(mysqlDB, mongoDB, mgo.NewConversationMongo, c.Conversation) }, - func() error { return NewTask(mysqlDB, mongoDB, mgo.NewS3Mongo, c.Object(config.Config.Object.Enable)) }, + func() error { return NewTask(mysqlDB, mongoDB, mgo.NewS3Mongo, c.Object(conf.Object.Enable)) }, func() error { return NewTask(mysqlDB, mongoDB, mgo.NewLogMongo, c.Log) }, func() error { return NewTask(mysqlDB, mongoDB, rtcmgo.NewSignal, c.SignalModel) }, From 532f6acb5d00f48a49cb90fdbd23ea5ac434cc29 Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:09:50 +0800 Subject: [PATCH 69/74] fix: fix the component logic (#2001) * optimization: change the configuration file from being read globally to being read independently. * optimization: change the configuration file from being read globally to being read independently. * optimization: change the configuration file from being read globally to being read independently. * optimization: config file changed to dependency injection. * fix: replace global config with dependency injection * fix: replace global config with dependency injection * fix: import the enough param * fix: import the enough param * fix: import the enough param * fix: fix the component check of path * fix: fix the kafka of tls is nil problem * fix: fix the TLS.CACrt is nil error * fix: fix the valiable shadows problem * fix: fix the comflect * optimization: message remove options. * fix: fix the param pass error * fix: find error * fix: find error * fix: find eror * fix: find error * fix: find error * fix: del the undifined func * fix: find error * fix: fix the error * fix: pass config * fix: find error * fix: find error * fix: find error * fix: find error * fix: find error * fix: fix the config * fix: fix the error * fix: fix the config pass error * fix: fix the eror * fix: fix the error * fix: fix the error * fix: fix the error * fix: find error * fix: fix the error * fix: fix the config * fix: add return err * fix: fix the err2 * fix: err * fix: fix the func * fix: del the chinese comment * fix: fix the func * fix: fix the gateway_test logic * fix: s3 * test --------- Co-authored-by: Gordon <1432970085@qq.com> Co-authored-by: withchao <993506633@qq.com> --- go.mod | 2 +- go.sum | 4 +- tools/component/component.go | 134 +++++++++++++++++++++++++++++------ 3 files changed, 116 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index 30bfcf146..556981a15 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.19 require ( firebase.google.com/go v3.13.0+incompatible github.com/OpenIMSDK/protocol v0.0.55 - github.com/OpenIMSDK/tools v0.0.36 + github.com/OpenIMSDK/tools v0.0.37 github.com/bwmarrin/snowflake v0.3.0 // indirect github.com/dtm-labs/rockscache v0.1.1 github.com/gin-gonic/gin v1.9.1 diff --git a/go.sum b/go.sum index 6277afac3..906eff45e 100644 --- a/go.sum +++ b/go.sum @@ -20,8 +20,8 @@ github.com/IBM/sarama v1.42.2 h1:VoY4hVIZ+WQJ8G9KNY/SQlWguBQXQ9uvFPOnrcu8hEw= github.com/IBM/sarama v1.42.2/go.mod h1:FLPGUGwYqEs62hq2bVG6Io2+5n+pS6s/WOXVKWSLFtE= github.com/OpenIMSDK/protocol v0.0.55 h1:eBjg8DyuhxGmuCUjpoZjg6MJJJXU/xJ3xJwFhrn34yA= github.com/OpenIMSDK/protocol v0.0.55/go.mod h1:F25dFrwrIx3lkNoiuf6FkCfxuwf8L4Z8UIsdTHP/r0Y= -github.com/OpenIMSDK/tools v0.0.36 h1:BT0q64l4f3QJDW16Rc0uJYt1gQFkiPoUQYQ33vo0EcE= -github.com/OpenIMSDK/tools v0.0.36/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= +github.com/OpenIMSDK/tools v0.0.37 h1:qvDqmA4RbEJtPjZouWCkVuf/pjm6Y8nUrG5iH2gcnOg= +github.com/OpenIMSDK/tools v0.0.37/go.mod h1:wBfR5CYmEyvxl03QJbTkhz1CluK6J4/lX0lviu8JAjE= github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= diff --git a/tools/component/component.go b/tools/component/component.go index bb3a2d46f..34d3dff6b 100644 --- a/tools/component/component.go +++ b/tools/component/component.go @@ -19,14 +19,12 @@ import ( "flag" "fmt" "os" + "strconv" "strings" "time" "github.com/IBM/sarama" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" "github.com/openimsdk/open-im-server/v3/pkg/common/kafka" "github.com/OpenIMSDK/tools/component" @@ -40,7 +38,7 @@ import ( const ( // defaultCfgPath is the default path of the configuration file. defaultCfgPath = "../../../../../config/config.yaml" - maxRetry = 300 + maxRetry = 100 ) var ( @@ -77,7 +75,11 @@ func main() { return } - configGetEnv() + err = configGetEnv(conf) + if err != nil { + fmt.Printf("configGetEnv failed,err:%v", err) + return + } checks := []checkFunc{ //{name: "Mysql", function: checkMysql}, @@ -100,9 +102,13 @@ func main() { if !check.flag { err = check.function(check.config) if err != nil { - component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, err)) allSuccess = false - + component.ErrorPrint(fmt.Sprintf("Starting %s failed:%v.", check.name, errs.Unwrap(err).Error())) + if !strings.Contains(errs.Unwrap(err).Error(), "connection refused") && + !strings.Contains(errs.Unwrap(err).Error(), "timeout waiting") { + component.ErrorPrint("Some components started failed!") + os.Exit(-1) + } } else { checks[index].flag = true component.SuccessPrint(fmt.Sprintf("%s connected successfully", check.name)) @@ -115,23 +121,38 @@ func main() { return } } + component.ErrorPrint("Some components started failed!") + os.Exit(-1) } // checkMongo checks the MongoDB connection without retries func checkMongo(config *config.GlobalConfig) error { - _, err := unrelation.NewMongo(config) + mongoStu := &component.Mongo{ + URL: config.Mongo.Uri, + Address: config.Mongo.Address, + Database: config.Mongo.Database, + Username: config.Mongo.Username, + Password: config.Mongo.Password, + MaxPoolSize: config.Mongo.MaxPoolSize, + } + err := component.CheckMongo(mongoStu) + return err } // checkRedis checks the Redis connection func checkRedis(config *config.GlobalConfig) error { - _, err := cache.NewRedis(config) + redisStu := &component.Redis{ + Address: config.Redis.Address, + Username: config.Redis.Username, + Password: config.Redis.Password, + } + err := component.CheckRedis(redisStu) return err } // checkMinio checks the MinIO connection func checkMinio(config *config.GlobalConfig) error { - // Check if MinIO is enabled if config.Object.Enable != "minio" { return errs.Wrap(errors.New("minio.Enable is empty")) @@ -150,7 +171,13 @@ func checkMinio(config *config.GlobalConfig) error { // checkZookeeper checks the Zookeeper connection func checkZookeeper(config *config.GlobalConfig) error { - _, err := zookeeper.NewZookeeperDiscoveryRegister(config) + zkStu := &component.Zookeeper{ + Schema: config.Zookeeper.Schema, + ZkAddr: config.Zookeeper.ZkAddr, + Username: config.Zookeeper.Username, + Password: config.Zookeeper.Password, + } + err := component.CheckZookeeper(zkStu) return err } @@ -241,16 +268,38 @@ func isTopicPresent(topic string, topics []string) bool { return false } -func configGetEnv() { - config.Config.Object.Minio.AccessKeyID = getEnv("MINIO_ACCESS_KEY_ID", config.Config.Object.Minio.AccessKeyID) - config.Config.Object.Minio.SecretAccessKey = getEnv("MINIO_SECRET_ACCESS_KEY", config.Config.Object.Minio.SecretAccessKey) - config.Config.Mongo.Uri = getEnv("MONGO_URI", config.Config.Mongo.Uri) - config.Config.Mongo.Username = getEnv("MONGO_OPENIM_USERNAME", config.Config.Mongo.Username) - config.Config.Mongo.Password = getEnv("MONGO_OPENIM_PASSWORD", config.Config.Mongo.Password) - config.Config.Kafka.Username = getEnv("KAFKA_USERNAME", config.Config.Kafka.Username) - config.Config.Kafka.Password = getEnv("KAFKA_PASSWORD", config.Config.Kafka.Password) - config.Config.Kafka.Addr = strings.Split(getEnv("KAFKA_ADDRESS", strings.Join(config.Config.Kafka.Addr, ",")), ",") - config.Config.Object.Minio.Endpoint = getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Config.Object.Minio.Endpoint) +func configGetEnv(config *config.GlobalConfig) error { + config.Mongo.Uri = getEnv("MONGO_URI", config.Mongo.Uri) + config.Mongo.Username = getEnv("MONGO_OPENIM_USERNAME", config.Mongo.Username) + config.Mongo.Password = getEnv("MONGO_OPENIM_PASSWORD", config.Mongo.Password) + config.Mongo.Address = getArrEnv("MONGO_ADDRESS", "MONGO_PORT", config.Mongo.Address) + config.Mongo.Database = getEnv("MONGO_DATABASE", config.Mongo.Database) + maxPoolSize, err := getEnvInt("MONGO_DATABASE", config.Mongo.MaxPoolSize) + if err != nil { + return err + } + config.Mongo.MaxPoolSize = maxPoolSize + + config.Redis.Username = getEnv("REDIS_USERNAME", config.Redis.Username) + config.Redis.Password = getEnv("REDIS_PASSWORD", config.Redis.Password) + config.Redis.Address = getArrEnv("REDIS_ADDRESS", "REDIS_PASSWORD", config.Redis.Address) + + config.Object.ApiURL = getEnv("OBJECT_APIURL", config.Object.ApiURL) + config.Object.Minio.Endpoint = getEnv("MINIO_ENDPOINT", config.Object.Minio.Endpoint) + config.Object.Minio.AccessKeyID = getEnv("MINIO_ACCESS_KEY_ID", config.Object.Minio.AccessKeyID) + config.Object.Minio.SecretAccessKey = getEnv("MINIO_SECRET_ACCESS_KEY", config.Object.Minio.SecretAccessKey) + config.Object.Minio.SignEndpoint = getEnv("MINIO_SIGN_ENDPOINT", config.Object.Minio.SignEndpoint) + + config.Zookeeper.Schema = getEnv("ZOOKEEPER_SCHEMA", config.Zookeeper.Schema) + config.Zookeeper.ZkAddr = getArrEnv("ZOOKEEPER_ADDRESS", "ZOOKEEPER_PORT", config.Zookeeper.ZkAddr) + config.Zookeeper.Username = getEnv("ZOOKEEPER_USERNAME", config.Zookeeper.Username) + config.Zookeeper.Password = getEnv("ZOOKEEPER_PASSWORD", config.Zookeeper.Password) + + config.Kafka.Username = getEnv("KAFKA_USERNAME", config.Kafka.Username) + config.Kafka.Password = getEnv("KAFKA_PASSWORD", config.Kafka.Password) + config.Kafka.Addr = getArrEnv("KAFKA_ADDRESS", "KAFKA_PORT", config.Kafka.Addr) + config.Object.Minio.Endpoint = getMinioAddr("MINIO_ENDPOINT", "MINIO_ADDRESS", "MINIO_PORT", config.Object.Minio.Endpoint) + return nil } func getMinioAddr(key1, key2, key3, fallback string) string { @@ -272,3 +321,46 @@ func getEnv(key, fallback string) string { } return fallback } + +// Helper function to get environment variable or default value +func getEnvInt(key string, fallback int) (int, error) { + if value, exists := os.LookupEnv(key); exists { + val, err := strconv.Atoi(value) + if err != nil { + return 0, errs.Wrap(err, "string to int failed") + } + return val, nil + } + return fallback, nil +} + +func getArrEnv(key1, key2 string, fallback []string) []string { + address, addrExists := os.LookupEnv(key1) + port, portExists := os.LookupEnv(key2) + + if addrExists && portExists { + addresses := strings.Split(address, ",") + for i, addr := range addresses { + addresses[i] = addr + ":" + port + } + return addresses + } + + if addrExists && !portExists { + addresses := strings.Split(address, ",") + for i, addr := range addresses { + addresses[i] = addr + ":" + "0" + } + return addresses + } + + if !addrExists && portExists { + result := make([]string, len(fallback)) + for i, addr := range fallback { + add := strings.Split(addr, ":") + result[i] = add[0] + ":" + port + } + return result + } + return fallback +} From c7dad1a5c1aa5421e43325476623b5d6394a9c8a Mon Sep 17 00:00:00 2001 From: OpenIM Bot <124379614+kubbot@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:58:05 +0800 Subject: [PATCH 70/74] cicd: bump League Patch (#2004) --- cmd/openim-api/main.go | 2 +- go.mod | 2 -- go.sum | 7 ----- internal/api/route.go | 28 ++++++++----------- internal/api/third.go | 3 +- internal/api/user.go | 1 - internal/msggateway/message_handler.go | 2 +- internal/msgtransfer/init.go | 7 ++++- internal/push/consumer_init.go | 1 + internal/push/offlinepush/fcm/push.go | 6 ++-- internal/push/push_rpc_server.go | 2 +- internal/rpc/conversation/conversaion.go | 2 +- internal/rpc/friend/friend.go | 27 +++++++----------- internal/rpc/group/group.go | 2 +- internal/rpc/msg/revoke.go | 4 +-- internal/rpc/msg/send.go | 5 ++-- internal/rpc/msg/server.go | 7 ++--- internal/rpc/third/third.go | 18 +++++------- internal/rpc/third/tool.go | 3 +- internal/rpc/user/user.go | 24 ++++++---------- internal/tools/cron_task.go | 9 +++--- internal/tools/cron_task_test.go | 5 ++-- pkg/common/cmd/api.go | 3 +- pkg/common/cmd/msg_gateway.go | 4 +-- pkg/common/cmd/msg_transfer.go | 4 +-- pkg/common/cmd/rpc.go | 11 +++----- pkg/common/db/cache/meta_cache.go | 6 ++-- pkg/common/db/cache/user.go | 8 ++---- pkg/common/db/controller/auth.go | 2 +- pkg/common/db/s3/cos/cos.go | 2 +- pkg/common/db/s3/minio/minio.go | 2 +- .../discoveryregister/discoveryregister.go | 2 +- .../discoveryregister_test.go | 3 +- .../kubernetes/kubernetes.go | 6 ++-- pkg/common/kafka/consumer.go | 11 ++++---- pkg/common/kafka/consumer_group.go | 3 +- pkg/common/kafka/producer.go | 3 +- pkg/common/startrpc/start.go | 22 ++++++--------- pkg/common/startrpc/start_test.go | 3 +- pkg/common/tls/tls.go | 2 +- pkg/rpcclient/msg.go | 2 +- pkg/rpcclient/notification/conversation.go | 2 +- pkg/rpcclient/notification/friend.go | 2 +- pkg/rpcclient/notification/group.go | 2 +- pkg/rpcclient/notification/msg.go | 2 +- pkg/rpcclient/notification/user.go | 2 +- pkg/rpcclient/third.go | 8 ++---- tools/up35/pkg/pkg.go | 3 +- 48 files changed, 114 insertions(+), 173 deletions(-) mode change 100755 => 100644 pkg/rpcclient/third.go diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index 9a307686d..ee19a5c60 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -15,10 +15,10 @@ package main import ( - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" _ "net/http/pprof" "github.com/openimsdk/open-im-server/v3/pkg/common/cmd" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" ) func main() { diff --git a/go.mod b/go.mod index 556981a15..38443ba5a 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require github.com/google/uuid v1.6.0 require ( github.com/IBM/sarama v1.42.2 github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible - github.com/aws/aws-sdk-go v1.49.21 github.com/go-redis/redis v6.15.9+incompatible github.com/redis/go-redis/v9 v9.4.0 github.com/spf13/pflag v1.0.5 @@ -95,7 +94,6 @@ require ( github.com/jinzhu/copier v0.3.5 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd // indirect github.com/klauspost/compress v1.17.4 // indirect diff --git a/go.sum b/go.sum index 906eff45e..aa41279af 100644 --- a/go.sum +++ b/go.sum @@ -31,8 +31,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go v1.49.21 h1:Rl8KW6HqkwzhATwvXhyr7vD4JFUMi7oXGAw9SrxxIFY= -github.com/aws/aws-sdk-go v1.49.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -205,10 +203,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= @@ -538,7 +532,6 @@ gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/internal/api/route.go b/internal/api/route.go index ce8e3a62b..92fa83fb0 100644 --- a/internal/api/route.go +++ b/internal/api/route.go @@ -17,10 +17,6 @@ package api import ( "context" "fmt" - kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "net" "net/http" "os" @@ -31,26 +27,26 @@ import ( "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/apiresp" + "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mw" "github.com/OpenIMSDK/tools/tokenverify" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" - "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" + ginprom "github.com/openimsdk/open-im-server/v3/pkg/common/ginprometheus" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "github.com/redis/go-redis/v9" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/tools/mw" - - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) func Start(config *config.GlobalConfig, port int, proPort int) error { diff --git a/internal/api/third.go b/internal/api/third.go index 190e0d540..30448ae4d 100644 --- a/internal/api/third.go +++ b/internal/api/third.go @@ -19,12 +19,11 @@ import ( "net/http" "strconv" - "github.com/gin-gonic/gin" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/a2r" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" + "github.com/gin-gonic/gin" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/api/user.go b/internal/api/user.go index 468432ee0..16b453e46 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -23,7 +23,6 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/gin-gonic/gin" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/internal/msggateway/message_handler.go b/internal/msggateway/message_handler.go index 208cd6bf7..2fbdd5683 100644 --- a/internal/msggateway/message_handler.go +++ b/internal/msggateway/message_handler.go @@ -16,7 +16,6 @@ package msggateway import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "sync" "github.com/OpenIMSDK/protocol/msg" @@ -26,6 +25,7 @@ import ( "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/utils" "github.com/go-playground/validator/v10" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "google.golang.org/protobuf/proto" ) diff --git a/internal/msgtransfer/init.go b/internal/msgtransfer/init.go index 5e9e80663..ee62db9c1 100644 --- a/internal/msgtransfer/init.go +++ b/internal/msgtransfer/init.go @@ -92,7 +92,12 @@ func StartTransfer(config *config.GlobalConfig, prometheusPort int) error { return msgTransfer.Start(prometheusPort, config) } -func NewMsgTransfer(config *config.GlobalConfig, msgDatabase controller.CommonMsgDatabase, conversationRpcClient *rpcclient.ConversationRpcClient, groupRpcClient *rpcclient.GroupRpcClient) (*MsgTransfer, error) { +func NewMsgTransfer( + config *config.GlobalConfig, + msgDatabase controller.CommonMsgDatabase, + conversationRpcClient *rpcclient.ConversationRpcClient, + groupRpcClient *rpcclient.GroupRpcClient, +) (*MsgTransfer, error) { historyCH, err := NewOnlineHistoryRedisConsumerHandler(config, msgDatabase, conversationRpcClient, groupRpcClient) if err != nil { return nil, err diff --git a/internal/push/consumer_init.go b/internal/push/consumer_init.go index 4ad77de2c..351b63f46 100644 --- a/internal/push/consumer_init.go +++ b/internal/push/consumer_init.go @@ -16,6 +16,7 @@ package push import ( "context" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) diff --git a/internal/push/offlinepush/fcm/push.go b/internal/push/offlinepush/fcm/push.go index 977254462..ed65a5af6 100644 --- a/internal/push/offlinepush/fcm/push.go +++ b/internal/push/offlinepush/fcm/push.go @@ -20,14 +20,12 @@ import ( firebase "firebase.google.com/go" "firebase.google.com/go/messaging" - "github.com/redis/go-redis/v9" - "google.golang.org/api/option" - "github.com/OpenIMSDK/protocol/constant" - "github.com/openimsdk/open-im-server/v3/internal/push/offlinepush" "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/redis/go-redis/v9" + "google.golang.org/api/option" ) const SinglePushCountLimit = 400 diff --git a/internal/push/push_rpc_server.go b/internal/push/push_rpc_server.go index ed2a0b1ef..2d2f95336 100644 --- a/internal/push/push_rpc_server.go +++ b/internal/push/push_rpc_server.go @@ -16,13 +16,13 @@ package push import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbpush "github.com/OpenIMSDK/protocol/push" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" diff --git a/internal/rpc/conversation/conversaion.go b/internal/rpc/conversation/conversaion.go index cc08de298..15e7998af 100644 --- a/internal/rpc/conversation/conversaion.go +++ b/internal/rpc/conversation/conversaion.go @@ -17,7 +17,6 @@ package conversation import ( "context" "errors" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "sort" "github.com/OpenIMSDK/protocol/constant" @@ -28,6 +27,7 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/rpc/friend/friend.go b/internal/rpc/friend/friend.go index ffdeee98f..6403a4159 100644 --- a/internal/rpc/friend/friend.go +++ b/internal/rpc/friend/friend.go @@ -16,33 +16,26 @@ package friend import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "github.com/OpenIMSDK/tools/tx" - - "github.com/OpenIMSDK/protocol/sdkws" - - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - - "github.com/OpenIMSDK/tools/log" - - "github.com/openimsdk/open-im-server/v3/pkg/common/convert" - "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" - - "google.golang.org/grpc" "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" + "github.com/OpenIMSDK/protocol/sdkws" registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/tx" "github.com/OpenIMSDK/tools/utils" - + "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" + "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" + "google.golang.org/grpc" ) type friendServer struct { @@ -118,7 +111,7 @@ func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, se // ok. func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.ApplyToAddFriendReq) (resp *pbfriend.ApplyToAddFriendResp, err error) { resp = &pbfriend.ApplyToAddFriendResp{} - if err := authverify.CheckAccessV3(ctx, req.FromUserID,s.config); err != nil { + if err := authverify.CheckAccessV3(ctx, req.FromUserID, s.config); err != nil { return nil, err } if req.ToUserID == req.FromUserID { @@ -141,7 +134,7 @@ func (s *friendServer) ApplyToAddFriend(ctx context.Context, req *pbfriend.Apply return nil, err } s.notificationSender.FriendApplicationAddNotification(ctx, req) - if err = CallbackAfterAddFriend(ctx,s.config ,req); err != nil && err != errs.ErrCallbackContinue { + if err = CallbackAfterAddFriend(ctx, s.config, req); err != nil && err != errs.ErrCallbackContinue { return nil, err } return resp, nil diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 325c1edce..81299a648 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -17,7 +17,6 @@ package group import ( "context" "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "math/big" "math/rand" "strconv" @@ -38,6 +37,7 @@ import ( "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/authverify" "github.com/openimsdk/open-im-server/v3/pkg/callbackstruct" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" diff --git a/internal/rpc/msg/revoke.go b/internal/rpc/msg/revoke.go index 4f844369f..5ac8804c2 100644 --- a/internal/rpc/msg/revoke.go +++ b/internal/rpc/msg/revoke.go @@ -19,8 +19,6 @@ import ( "encoding/json" "time" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" @@ -28,7 +26,7 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" - + "github.com/openimsdk/open-im-server/v3/pkg/authverify" unrelationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/unrelation" ) diff --git a/internal/rpc/msg/send.go b/internal/rpc/msg/send.go index 4bac4d1e0..949a880b9 100644 --- a/internal/rpc/msg/send.go +++ b/internal/rpc/msg/send.go @@ -17,9 +17,6 @@ package msg import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" - "github.com/OpenIMSDK/protocol/constant" pbconversation "github.com/OpenIMSDK/protocol/conversation" pbmsg "github.com/OpenIMSDK/protocol/msg" @@ -29,6 +26,8 @@ import ( "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" + "github.com/openimsdk/open-im-server/v3/pkg/msgprocessor" ) func (m *msgServer) SendMsg(ctx context.Context, req *pbmsg.SendMsgReq) (resp *pbmsg.SendMsgResp, error error) { diff --git a/internal/rpc/msg/server.go b/internal/rpc/msg/server.go index 5b7cd2f66..72e0b065a 100644 --- a/internal/rpc/msg/server.go +++ b/internal/rpc/msg/server.go @@ -15,20 +15,17 @@ package msg import ( - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/conversation" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/tools/discoveryregistry" - + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/localcache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) type ( diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index 2bccb5c78..f1bf2f86c 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -20,23 +20,19 @@ import ( "net/url" "time" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/cos" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/minio" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3/oss" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" + "google.golang.org/grpc" ) func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error { diff --git a/internal/rpc/third/tool.go b/internal/rpc/third/tool.go index 6591134d6..d8491d354 100644 --- a/internal/rpc/third/tool.go +++ b/internal/rpc/third/tool.go @@ -21,11 +21,10 @@ import ( "strings" "unicode/utf8" - "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mcontext" + "github.com/openimsdk/open-im-server/v3/pkg/authverify" ) func toPbMapArray(m map[string][]string) []*third.KeyValues { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index 02e641d20..ae891b7dd 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -21,34 +21,26 @@ import ( "strings" "time" - "github.com/OpenIMSDK/tools/pagination" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - - "github.com/OpenIMSDK/tools/tx" - - "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" - "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + pbuser "github.com/OpenIMSDK/protocol/user" + registry "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - + "github.com/OpenIMSDK/tools/pagination" + "github.com/OpenIMSDK/tools/tx" + "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/authverify" - "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" - - registry "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/mgo" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" tablerelation "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" + "github.com/openimsdk/open-im-server/v3/pkg/common/db/unrelation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient/notification" - - pbuser "github.com/OpenIMSDK/protocol/user" - "github.com/OpenIMSDK/tools/utils" "google.golang.org/grpc" ) diff --git a/internal/tools/cron_task.go b/internal/tools/cron_task.go index ce87e9b90..7535e9b96 100644 --- a/internal/tools/cron_task.go +++ b/internal/tools/cron_task.go @@ -23,11 +23,10 @@ import ( "time" "github.com/OpenIMSDK/tools/errs" - "github.com/redis/go-redis/v9" - "github.com/robfig/cron/v3" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" + "github.com/redis/go-redis/v9" + "github.com/robfig/cron/v3" ) func StartTask(config *config.GlobalConfig) error { @@ -48,13 +47,13 @@ func StartTask(config *config.GlobalConfig) error { // register cron tasks var crontab = cron.New() fmt.Printf("Start chatRecordsClearTime cron task, cron config: %s\n", config.ChatRecordsClearTime) - _, err = crontab.AddFunc(config.ChatRecordsClearTime, cronWrapFunc(config,rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) + _, err = crontab.AddFunc(config.ChatRecordsClearTime, cronWrapFunc(config, rdb, "cron_clear_msg_and_fix_seq", msgTool.AllConversationClearMsgAndFixSeq)) if err != nil { return errs.Wrap(err) } fmt.Printf("Start msgDestruct cron task, cron config: %s\n", config.MsgDestructTime) - _, err = crontab.AddFunc(config.MsgDestructTime, cronWrapFunc(config,rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) + _, err = crontab.AddFunc(config.MsgDestructTime, cronWrapFunc(config, rdb, "cron_conversations_destruct_msgs", msgTool.ConversationsDestructMsgs)) if err != nil { return errs.Wrap(err, "cron_conversations_destruct_msgs") } diff --git a/internal/tools/cron_task_test.go b/internal/tools/cron_task_test.go index fcae5a5f6..17346b1c5 100644 --- a/internal/tools/cron_task_test.go +++ b/internal/tools/cron_task_test.go @@ -17,14 +17,15 @@ package tools import ( "flag" "fmt" - "github.com/OpenIMSDK/tools/errs" - "gopkg.in/yaml.v3" "math/rand" "os" "sync" "testing" "time" + "github.com/OpenIMSDK/tools/errs" + "gopkg.in/yaml.v3" + "github.com/redis/go-redis/v9" "github.com/robfig/cron/v3" "github.com/stretchr/testify/assert" diff --git a/pkg/common/cmd/api.go b/pkg/common/cmd/api.go index 859508ce3..bee16fdad 100644 --- a/pkg/common/cmd/api.go +++ b/pkg/common/cmd/api.go @@ -17,9 +17,8 @@ package cmd import ( "github.com/OpenIMSDK/protocol/constant" "github.com/openimsdk/open-im-server/v3/internal/api" - "github.com/spf13/cobra" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/spf13/cobra" ) type ApiCmd struct { diff --git a/pkg/common/cmd/msg_gateway.go b/pkg/common/cmd/msg_gateway.go index 3a939fa97..37aedd933 100644 --- a/pkg/common/cmd/msg_gateway.go +++ b/pkg/common/cmd/msg_gateway.go @@ -17,11 +17,9 @@ package cmd import ( "log" - "github.com/spf13/cobra" - "github.com/OpenIMSDK/protocol/constant" - "github.com/openimsdk/open-im-server/v3/internal/msggateway" + "github.com/spf13/cobra" ) type MsgGatewayCmd struct { diff --git a/pkg/common/cmd/msg_transfer.go b/pkg/common/cmd/msg_transfer.go index e46b66b52..d98154f3a 100644 --- a/pkg/common/cmd/msg_transfer.go +++ b/pkg/common/cmd/msg_transfer.go @@ -16,10 +16,10 @@ package cmd import ( "fmt" - "github.com/OpenIMSDK/protocol/constant" - "github.com/spf13/cobra" + "github.com/OpenIMSDK/protocol/constant" "github.com/openimsdk/open-im-server/v3/internal/msgtransfer" + "github.com/spf13/cobra" ) type MsgTransferCmd struct { diff --git a/pkg/common/cmd/rpc.go b/pkg/common/cmd/rpc.go index 5199524e7..9c6dbddd6 100644 --- a/pkg/common/cmd/rpc.go +++ b/pkg/common/cmd/rpc.go @@ -16,17 +16,14 @@ package cmd import ( "errors" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" - "github.com/spf13/cobra" - "google.golang.org/grpc" - - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/OpenIMSDK/tools/discoveryregistry" - + "github.com/OpenIMSDK/tools/errs" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/startrpc" + "github.com/spf13/cobra" + "google.golang.org/grpc" ) type rpcInitFuc func(config *config2.GlobalConfig, disCov discoveryregistry.SvcDiscoveryRegistry, server *grpc.Server) error diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 1fbb6c3b6..00dadfff8 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -21,13 +21,11 @@ import ( "fmt" "time" - "github.com/OpenIMSDK/tools/mw/specialerror" - - "github.com/dtm-labs/rockscache" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" + "github.com/OpenIMSDK/tools/mw/specialerror" "github.com/OpenIMSDK/tools/utils" + "github.com/dtm-labs/rockscache" ) const ( diff --git a/pkg/common/db/cache/user.go b/pkg/common/db/cache/user.go index 34c220624..837e7a5bc 100644 --- a/pkg/common/db/cache/user.go +++ b/pkg/common/db/cache/user.go @@ -22,16 +22,12 @@ import ( "strconv" "time" - relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" - - "github.com/OpenIMSDK/tools/log" - "github.com/OpenIMSDK/protocol/constant" - "github.com/OpenIMSDK/protocol/user" "github.com/OpenIMSDK/tools/errs" - + "github.com/OpenIMSDK/tools/log" "github.com/dtm-labs/rockscache" + relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/redis/go-redis/v9" ) diff --git a/pkg/common/db/controller/auth.go b/pkg/common/db/controller/auth.go index 19bea5981..7cafa1c48 100644 --- a/pkg/common/db/controller/auth.go +++ b/pkg/common/db/controller/auth.go @@ -16,13 +16,13 @@ package controller import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/tokenverify" "github.com/golang-jwt/jwt/v4" "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/cache" ) diff --git a/pkg/common/db/s3/cos/cos.go b/pkg/common/db/s3/cos/cos.go index a7c26fcc1..9852d2a98 100644 --- a/pkg/common/db/s3/cos/cos.go +++ b/pkg/common/db/s3/cos/cos.go @@ -23,13 +23,13 @@ import ( "encoding/json" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "net/http" "net/url" "strconv" "strings" "time" + "github.com/OpenIMSDK/tools/errs" "github.com/openimsdk/open-im-server/v3/pkg/common/db/s3" "github.com/tencentyun/cos-go-sdk-v5" ) diff --git a/pkg/common/db/s3/minio/minio.go b/pkg/common/db/s3/minio/minio.go index cd77948d4..10526998f 100644 --- a/pkg/common/db/s3/minio/minio.go +++ b/pkg/common/db/s3/minio/minio.go @@ -18,7 +18,6 @@ import ( "context" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "io" "net/http" "net/url" @@ -30,6 +29,7 @@ import ( "time" "unsafe" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7/pkg/credentials" diff --git a/pkg/common/discoveryregister/discoveryregister.go b/pkg/common/discoveryregister/discoveryregister.go index c43583a80..d3acf85f6 100644 --- a/pkg/common/discoveryregister/discoveryregister.go +++ b/pkg/common/discoveryregister/discoveryregister.go @@ -16,11 +16,11 @@ package discoveryregister import ( "errors" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "os" "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/direct" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/kubernetes" "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister/zookeeper" diff --git a/pkg/common/discoveryregister/discoveryregister_test.go b/pkg/common/discoveryregister/discoveryregister_test.go index 7d4fa53cd..08aa5d5d5 100644 --- a/pkg/common/discoveryregister/discoveryregister_test.go +++ b/pkg/common/discoveryregister/discoveryregister_test.go @@ -15,10 +15,11 @@ package discoveryregister import ( - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "os" "testing" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/stretchr/testify/assert" ) diff --git a/pkg/common/discoveryregister/kubernetes/kubernetes.go b/pkg/common/discoveryregister/kubernetes/kubernetes.go index b5d603fd1..83af53db0 100644 --- a/pkg/common/discoveryregister/kubernetes/kubernetes.go +++ b/pkg/common/discoveryregister/kubernetes/kubernetes.go @@ -22,12 +22,10 @@ import ( "strconv" "strings" - "github.com/stathat/consistent" - - "google.golang.org/grpc" - "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/log" + "github.com/stathat/consistent" + "google.golang.org/grpc" ) // K8sDR represents the Kubernetes service discovery and registration client. diff --git a/pkg/common/kafka/consumer.go b/pkg/common/kafka/consumer.go index 664e5d468..6627c21ae 100644 --- a/pkg/common/kafka/consumer.go +++ b/pkg/common/kafka/consumer.go @@ -17,10 +17,9 @@ package kafka import ( "sync" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" ) type Consumer struct { @@ -31,7 +30,7 @@ type Consumer struct { Consumer sarama.Consumer } -func NewKafkaConsumer(addr []string, topic string, config *config.GlobalConfig) (*Consumer,error) { +func NewKafkaConsumer(addr []string, topic string, config *config.GlobalConfig) (*Consumer, error) { p := Consumer{} p.Topic = topic p.addr = addr @@ -51,9 +50,9 @@ func NewKafkaConsumer(addr []string, topic string, config *config.GlobalConfig) InsecureSkipVerify: false, } } - err:=SetupTLSConfig(consumerConfig, tlsConfig) - if err!=nil{ - return nil,err + err := SetupTLSConfig(consumerConfig, tlsConfig) + if err != nil { + return nil, err } consumer, err := sarama.NewConsumer(p.addr, consumerConfig) if err != nil { diff --git a/pkg/common/kafka/consumer_group.go b/pkg/common/kafka/consumer_group.go index b6b4435ab..95794aacb 100644 --- a/pkg/common/kafka/consumer_group.go +++ b/pkg/common/kafka/consumer_group.go @@ -17,12 +17,11 @@ package kafka import ( "context" "errors" + "strings" "github.com/IBM/sarama" "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" - - "strings" ) type MConsumerGroup struct { diff --git a/pkg/common/kafka/producer.go b/pkg/common/kafka/producer.go index afc53b35a..8ee938b51 100644 --- a/pkg/common/kafka/producer.go +++ b/pkg/common/kafka/producer.go @@ -22,10 +22,9 @@ import ( "strings" "time" - "github.com/OpenIMSDK/tools/errs" - "github.com/IBM/sarama" "github.com/OpenIMSDK/protocol/constant" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/mcontext" "google.golang.org/protobuf/proto" diff --git a/pkg/common/startrpc/start.go b/pkg/common/startrpc/start.go index 8af894ac0..bf95509d1 100644 --- a/pkg/common/startrpc/start.go +++ b/pkg/common/startrpc/start.go @@ -18,7 +18,6 @@ import ( "context" "errors" "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "net" "net/http" "os" @@ -28,25 +27,20 @@ import ( "syscall" "time" + "github.com/OpenIMSDK/tools/discoveryregistry" "github.com/OpenIMSDK/tools/errs" - + "github.com/OpenIMSDK/tools/mw" + "github.com/OpenIMSDK/tools/network" + grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" + kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" + "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" - - config2 "github.com/openimsdk/open-im-server/v3/pkg/common/config" - "github.com/openimsdk/open-im-server/v3/pkg/common/prommetrics" - - grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - - kdisc "github.com/openimsdk/open-im-server/v3/pkg/common/discoveryregister" - - "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/mw" - "github.com/OpenIMSDK/tools/network" ) // Start rpc server. diff --git a/pkg/common/startrpc/start_test.go b/pkg/common/startrpc/start_test.go index e5e37e221..754fc9c50 100644 --- a/pkg/common/startrpc/start_test.go +++ b/pkg/common/startrpc/start_test.go @@ -16,11 +16,12 @@ package startrpc import ( "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "net" "testing" "time" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" + "github.com/OpenIMSDK/tools/discoveryregistry" "google.golang.org/grpc" ) diff --git a/pkg/common/tls/tls.go b/pkg/common/tls/tls.go index 736913758..9666ed9c8 100755 --- a/pkg/common/tls/tls.go +++ b/pkg/common/tls/tls.go @@ -49,7 +49,7 @@ func readEncryptablePEMBlock(path string, pwd []byte) ([]byte, error) { } // NewTLSConfig setup the TLS config from general config file. -func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte, insecureSkipVerify bool) (*tls.Config,error) { +func NewTLSConfig(clientCertFile, clientKeyFile, caCertFile string, keyPwd []byte, insecureSkipVerify bool) (*tls.Config, error) { tlsConfig := tls.Config{} if clientCertFile != "" && clientKeyFile != "" { diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 3db39925e..61679f28a 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -18,12 +18,12 @@ import ( "context" "encoding/json" "fmt" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/config" diff --git a/pkg/rpcclient/notification/conversation.go b/pkg/rpcclient/notification/conversation.go index 115fb81a2..1544d6a1f 100644 --- a/pkg/rpcclient/notification/conversation.go +++ b/pkg/rpcclient/notification/conversation.go @@ -16,10 +16,10 @@ package notification import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/rpcclient/notification/friend.go b/pkg/rpcclient/notification/friend.go index 31426da31..dafca055a 100644 --- a/pkg/rpcclient/notification/friend.go +++ b/pkg/rpcclient/notification/friend.go @@ -16,12 +16,12 @@ package notification import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbfriend "github.com/OpenIMSDK/protocol/friend" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/mcontext" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/convert" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 5500f4f43..5fd794be2 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -17,7 +17,6 @@ package notification import ( "context" "fmt" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" pbgroup "github.com/OpenIMSDK/protocol/group" @@ -27,6 +26,7 @@ import ( "github.com/OpenIMSDK/tools/mcontext" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/authverify" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" diff --git a/pkg/rpcclient/notification/msg.go b/pkg/rpcclient/notification/msg.go index 19819e7b7..beaef1d96 100644 --- a/pkg/rpcclient/notification/msg.go +++ b/pkg/rpcclient/notification/msg.go @@ -16,10 +16,10 @@ package notification import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" ) diff --git a/pkg/rpcclient/notification/user.go b/pkg/rpcclient/notification/user.go index 204b13e61..610967fd5 100644 --- a/pkg/rpcclient/notification/user.go +++ b/pkg/rpcclient/notification/user.go @@ -16,10 +16,10 @@ package notification import ( "context" - "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/sdkws" + "github.com/openimsdk/open-im-server/v3/pkg/common/config" "github.com/openimsdk/open-im-server/v3/pkg/common/db/controller" relationtb "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" "github.com/openimsdk/open-im-server/v3/pkg/rpcclient" diff --git a/pkg/rpcclient/third.go b/pkg/rpcclient/third.go old mode 100755 new mode 100644 index 50d545a64..a1f8ae180 --- a/pkg/rpcclient/third.go +++ b/pkg/rpcclient/third.go @@ -16,15 +16,13 @@ package rpcclient import ( "context" - "github.com/OpenIMSDK/tools/errs" "net/url" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - "github.com/OpenIMSDK/protocol/third" "github.com/OpenIMSDK/tools/discoveryregistry" - + "github.com/OpenIMSDK/tools/errs" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" "github.com/openimsdk/open-im-server/v3/pkg/common/config" util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "google.golang.org/grpc" diff --git a/tools/up35/pkg/pkg.go b/tools/up35/pkg/pkg.go index 1348172d2..49f471204 100644 --- a/tools/up35/pkg/pkg.go +++ b/tools/up35/pkg/pkg.go @@ -18,12 +18,13 @@ import ( "context" "errors" "fmt" - "github.com/OpenIMSDK/tools/errs" "log" "os" "reflect" "strconv" + "github.com/OpenIMSDK/tools/errs" + "gopkg.in/yaml.v3" "github.com/go-sql-driver/mysql" From 52b8efba73522f8cbf0557a0f86f3a125387eeac Mon Sep 17 00:00:00 2001 From: Brabem <69128477+luhaoling@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:20:07 +0800 Subject: [PATCH 71/74] fix: wrap the error of group user and thrid (#2005) * fix: wrap the error of group user and thrid * fix: del the chinese comment * fix: fix the make_lint error * fix: fix the ApiTest error --- internal/rpc/group/group.go | 8 ++++---- internal/rpc/third/s3.go | 4 ++-- internal/rpc/third/third.go | 7 ++++--- internal/rpc/user/user.go | 15 ++++++--------- pkg/common/db/cache/meta_cache.go | 4 ++-- pkg/common/db/cache/msg.go | 2 +- pkg/common/db/mgo/object.go | 3 ++- pkg/common/db/mgo/user.go | 14 +++++++------- pkg/common/db/s3/minio/image.go | 18 +++++++++--------- pkg/common/db/unrelation/mongo.go | 7 +++---- .../discoveryregister/direct/directconn.go | 4 ++-- pkg/rpcclient/msg.go | 5 +++-- tools/up35/pkg/pkg.go | 8 ++++---- 13 files changed, 49 insertions(+), 50 deletions(-) diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index 81299a648..d44c79b45 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -120,12 +120,12 @@ func (s *groupServer) NotificationUserInfoUpdate(ctx context.Context, req *pbgro groupIDs = append(groupIDs, member.GroupID) } for _, groupID := range groupIDs { - if err := s.Notification.GroupMemberInfoSetNotification(ctx, groupID, req.UserID); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate setGroupMemberInfo notification failed", err, "groupID", groupID) + if err = s.Notification.GroupMemberInfoSetNotification(ctx, groupID, req.UserID); err != nil { + return nil, err } } - if err := s.db.DeleteGroupMemberHash(ctx, groupIDs); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate DeleteGroupMemberHash", err, "groupID", groupIDs) + if err = s.db.DeleteGroupMemberHash(ctx, groupIDs); err != nil { + return nil, err } return &pbgroup.NotificationUserInfoUpdateResp{}, nil diff --git a/internal/rpc/third/s3.go b/internal/rpc/third/s3.go index 1975163e5..f79b73a99 100644 --- a/internal/rpc/third/s3.go +++ b/internal/rpc/third/s3.go @@ -209,7 +209,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF } uid, err := uuid.NewRandom() if err != nil { - return nil, err + return nil, errs.Wrap(err, "uuid NewRandom failed") } if key == "" { date := time.Now().Format("20060102") @@ -224,7 +224,7 @@ func (t *thirdServer) InitiateFormData(ctx context.Context, req *third.InitiateF } mateData, err := json.Marshal(&mate) if err != nil { - return nil, err + return nil, errs.Wrap(err, "marshal failed") } resp, err := t.s3dataBase.FormData(ctx, key, req.Size, req.ContentType, duration) if err != nil { diff --git a/internal/rpc/third/third.go b/internal/rpc/third/third.go index f1bf2f86c..5f6fc6c34 100644 --- a/internal/rpc/third/third.go +++ b/internal/rpc/third/third.go @@ -17,6 +17,7 @@ package third import ( "context" "fmt" + "github.com/OpenIMSDK/tools/errs" "net/url" "time" @@ -50,9 +51,9 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg } apiURL := config.Object.ApiURL if apiURL == "" { - return fmt.Errorf("api url is empty") + return errs.Wrap(fmt.Errorf("api is empty")) } - if _, parseErr := url.Parse(config.Object.ApiURL); parseErr != nil { + if _, err := url.Parse(config.Object.ApiURL); err != nil { return err } if apiURL[len(apiURL)-1] != '/' { @@ -63,7 +64,7 @@ func Start(config *config.GlobalConfig, client discoveryregistry.SvcDiscoveryReg if err != nil { return err } - // 根据配置文件策略选择 oss 方式 + // Select the oss method according to the profile policy enable := config.Object.Enable var o s3.Interface switch enable { diff --git a/internal/rpc/user/user.go b/internal/rpc/user/user.go index ae891b7dd..3d13cd7b6 100644 --- a/internal/rpc/user/user.go +++ b/internal/rpc/user/user.go @@ -16,7 +16,7 @@ package user import ( "context" - "errors" + "fmt" "math/rand" "strings" "time" @@ -70,7 +70,7 @@ func Start(config *config.GlobalConfig, client registry.SvcDiscoveryRegistry, se } users := make([]*tablerelation.UserModel, 0) if len(config.IMAdmin.UserID) != len(config.IMAdmin.Nickname) { - return errors.New("len(s.config.AppNotificationAdmin.AppManagerUid) != len(s.config.AppNotificationAdmin.Nickname)") + return errs.Wrap(fmt.Errorf("the count of ImAdmin.UserID is not equal to the count of ImAdmin.Nickname")) } for k, v := range config.IMAdmin.UserID { users = append(users, &tablerelation.UserModel{UserID: v, Nickname: config.IMAdmin.Nickname[k], AppMangerLevel: constant.AppNotificationAdmin}) @@ -105,9 +105,6 @@ func (s *userServer) GetDesignateUsers(ctx context.Context, req *pbuser.GetDesig return nil, err } resp.UsersInfo = convert.UsersDB2Pb(users) - if err != nil { - return nil, err - } return resp, nil } @@ -131,7 +128,7 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI } if req.UserInfo.Nickname != "" || req.UserInfo.FaceURL != "" { if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate", err) + return nil, err } } for _, friendID := range friends { @@ -141,7 +138,7 @@ func (s *userServer) UpdateUserInfo(ctx context.Context, req *pbuser.UpdateUserI return nil, err } if err = s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate", err, "userID", req.UserInfo.UserID) + return nil, err } return resp, nil } @@ -166,7 +163,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse } if req.UserInfo.Nickname != nil || req.UserInfo.FaceURL != nil { if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate", err) + return nil, err } } for _, friendID := range friends { @@ -176,7 +173,7 @@ func (s *userServer) UpdateUserInfoEx(ctx context.Context, req *pbuser.UpdateUse return nil, err } if err := s.groupRpcClient.NotificationUserInfoUpdate(ctx, req.UserInfo.UserID); err != nil { - log.ZError(ctx, "NotificationUserInfoUpdate", err, "userID", req.UserInfo.UserID) + return nil, err } return resp, nil } diff --git a/pkg/common/db/cache/meta_cache.go b/pkg/common/db/cache/meta_cache.go index 00dadfff8..4d4f077b6 100644 --- a/pkg/common/db/cache/meta_cache.go +++ b/pkg/common/db/cache/meta_cache.go @@ -129,7 +129,7 @@ func getCache[T any](ctx context.Context, rcClient *rockscache.Client, key strin v, err := rcClient.Fetch2(ctx, key, expire, func() (s string, err error) { t, err = fn(ctx) if err != nil { - return "", errs.Wrap(err) + return "", err } bs, err := json.Marshal(t) if err != nil { @@ -204,7 +204,7 @@ func batchGetCache2[T any, K comparable]( fns func(ctx context.Context, key K) (T, error), ) ([]T, error) { if len(keys) == 0 { - return nil, errs.ErrArgs.Wrap("groupID is empty") + return nil, nil } res := make([]T, 0, len(keys)) for _, key := range keys { diff --git a/pkg/common/db/cache/msg.go b/pkg/common/db/cache/msg.go index 1266875f1..9b488c1bf 100644 --- a/pkg/common/db/cache/msg.go +++ b/pkg/common/db/cache/msg.go @@ -471,7 +471,7 @@ func (c *msgCache) ParallelSetMessageToCache(ctx context.Context, conversationID err := wg.Wait() if err != nil { - return 0, err + return 0, errs.Wrap(err, "wg.Wait failed") } return len(msgs), nil diff --git a/pkg/common/db/mgo/object.go b/pkg/common/db/mgo/object.go index 4c333afd6..a527fa60d 100644 --- a/pkg/common/db/mgo/object.go +++ b/pkg/common/db/mgo/object.go @@ -16,6 +16,7 @@ package mgo import ( "context" + "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/mgoutil" "github.com/openimsdk/open-im-server/v3/pkg/common/db/table/relation" @@ -33,7 +34,7 @@ func NewS3Mongo(db *mongo.Database) (relation.ObjectInfoModelInterface, error) { Options: options.Index().SetUnique(true), }) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return &S3Mongo{coll: coll}, nil } diff --git a/pkg/common/db/mgo/user.go b/pkg/common/db/mgo/user.go index dcdc14d4a..9ca2eb178 100644 --- a/pkg/common/db/mgo/user.go +++ b/pkg/common/db/mgo/user.go @@ -157,7 +157,7 @@ func (u *UserMgo) AddUserCommand(ctx context.Context, userID string, Type int32, } _, err := collection.InsertOne(ctx, doc) - return err + return errs.Wrap(err) } func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int32, UUID string) error { @@ -170,7 +170,7 @@ func (u *UserMgo) DeleteUserCommand(ctx context.Context, userID string, Type int // No records found to update return errs.Wrap(errs.ErrRecordNotFound) } - return err + return errs.Wrap(err) } func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int32, UUID string, val map[string]any) error { if len(val) == 0 { @@ -184,7 +184,7 @@ func (u *UserMgo) UpdateUserCommand(ctx context.Context, userID string, Type int result, err := collection.UpdateOne(ctx, filter, update) if err != nil { - return err + return errs.Wrap(err) } if result.MatchedCount == 0 { @@ -233,7 +233,7 @@ func (u *UserMgo) GetUserCommand(ctx context.Context, userID string, Type int32) } if err := cursor.Err(); err != nil { - return nil, err + return nil, errs.Wrap(err) } return commands, nil @@ -244,7 +244,7 @@ func (u *UserMgo) GetAllUserCommand(ctx context.Context, userID string) ([]*user cursor, err := collection.Find(ctx, filter) if err != nil { - return nil, err + return nil, errs.Wrap(err) } defer cursor.Close(ctx) @@ -261,7 +261,7 @@ func (u *UserMgo) GetAllUserCommand(ctx context.Context, userID string) ([]*user } if err := cursor.Decode(&document); err != nil { - return nil, err + return nil, errs.Wrap(err) } commandInfo := &user.AllCommandInfoResp{ @@ -276,7 +276,7 @@ func (u *UserMgo) GetAllUserCommand(ctx context.Context, userID string) ([]*user } if err := cursor.Err(); err != nil { - return nil, err + return nil, errs.Wrap(err) } return commands, nil } diff --git a/pkg/common/db/s3/minio/image.go b/pkg/common/db/s3/minio/image.go index 71db1ea51..3223993f4 100644 --- a/pkg/common/db/s3/minio/image.go +++ b/pkg/common/db/s3/minio/image.go @@ -47,27 +47,27 @@ func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { imgWidth := bounds.Max.X imgHeight := bounds.Max.Y - // 计算缩放比例 + // Calculating scaling scaleWidth := float64(maxWidth) / float64(imgWidth) scaleHeight := float64(maxHeight) / float64(imgHeight) - // 如果都为0,则不缩放,返回原始图片 + // If both are 0, then no scaling is done and the original image is returned if maxWidth == 0 && maxHeight == 0 { return img } - // 如果宽度和高度都大于0,则选择较小的缩放比例,以保持宽高比 + // If both width and height are greater than 0, select a smaller zoom ratio to maintain the aspect ratio if maxWidth > 0 && maxHeight > 0 { scale := scaleWidth if scaleHeight < scaleWidth { scale = scaleHeight } - // 计算缩略图尺寸 + // Calculate Thumbnail Size thumbnailWidth := int(float64(imgWidth) * scale) thumbnailHeight := int(float64(imgHeight) * scale) - // 使用"image"库的Resample方法生成缩略图 + // Thumbnails are generated using the Resample method of the "image" library. thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) for y := 0; y < thumbnailHeight; y++ { for x := 0; x < thumbnailWidth; x++ { @@ -80,12 +80,12 @@ func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { return thumbnail } - // 如果只指定了宽度或高度,则根据最大不超过的规则生成缩略图 + // If only width or height is specified, thumbnails are generated based on the maximum not to exceed rule if maxWidth > 0 { thumbnailWidth := maxWidth thumbnailHeight := int(float64(imgHeight) * scaleWidth) - // 使用"image"库的Resample方法生成缩略图 + // Thumbnails are generated using the Resample method of the "image" library. thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) for y := 0; y < thumbnailHeight; y++ { for x := 0; x < thumbnailWidth; x++ { @@ -102,7 +102,7 @@ func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { thumbnailWidth := int(float64(imgWidth) * scaleHeight) thumbnailHeight := maxHeight - // 使用"image"库的Resample方法生成缩略图 + // Thumbnails are generated using the Resample method of the "image" library. thumbnail := image.NewRGBA(image.Rect(0, 0, thumbnailWidth, thumbnailHeight)) for y := 0; y < thumbnailHeight; y++ { for x := 0; x < thumbnailWidth; x++ { @@ -115,6 +115,6 @@ func resizeImage(img image.Image, maxWidth, maxHeight int) image.Image { return thumbnail } - // 默认情况下,返回原始图片 + // By default, the original image is returned return img } diff --git a/pkg/common/db/unrelation/mongo.go b/pkg/common/db/unrelation/mongo.go index 363e97867..834e81237 100644 --- a/pkg/common/db/unrelation/mongo.go +++ b/pkg/common/db/unrelation/mongo.go @@ -102,12 +102,11 @@ func buildMongoURI(config *config.GlobalConfig) string { maxPoolSize = fmt.Sprint(config.Mongo.MaxPoolSize) } - uriFormat := "mongodb://%s/%s?maxPoolSize=%s" if username != "" && password != "" { - uriFormat = "mongodb://%s:%s@%s/%s?maxPoolSize=%s" - return fmt.Sprintf(uriFormat, username, password, address, database, maxPoolSize) + + return fmt.Sprintf("mongodb://%s:%s@%s/%s?maxPoolSize=%s", username, password, address, database, maxPoolSize) } - return fmt.Sprintf(uriFormat, address, database, maxPoolSize) + return fmt.Sprintf("mongodb://%s/%s?maxPoolSize=%s", address, database, maxPoolSize) } func shouldRetry(err error) bool { diff --git a/pkg/common/discoveryregister/direct/directconn.go b/pkg/common/discoveryregister/direct/directconn.go index ced209602..df03825e5 100644 --- a/pkg/common/discoveryregister/direct/directconn.go +++ b/pkg/common/discoveryregister/direct/directconn.go @@ -100,7 +100,7 @@ func (cd *ConnDirect) GetConns(ctx context.Context, for _, port := range ports { conn, err := cd.dialServiceWithoutResolver(ctx, fmt.Sprintf(cd.config.Rpc.ListenIP+":%d", port), append(cd.additionalOpts, opts...)...) if err != nil { - fmt.Printf("connect to port %d failed,serviceName %s, IP %s\n", port, serviceName, cd.config.Rpc.ListenIP) + return nil, errs.Wrap(fmt.Errorf("connect to port %d failed,serviceName %s, IP %s", port, serviceName, cd.config.Rpc.ListenIP)) } connections = append(connections, conn) } @@ -166,7 +166,7 @@ func (cd *ConnDirect) dialServiceWithoutResolver(ctx context.Context, address st conn, err := grpc.DialContext(ctx, address, options...) if err != nil { - return nil, err + return nil, errs.Wrap(err) } return conn, nil } diff --git a/pkg/rpcclient/msg.go b/pkg/rpcclient/msg.go index 61679f28a..4daf897a1 100644 --- a/pkg/rpcclient/msg.go +++ b/pkg/rpcclient/msg.go @@ -18,12 +18,13 @@ import ( "context" "encoding/json" "fmt" + "github.com/OpenIMSDK/tools/errs" + util "github.com/openimsdk/open-im-server/v3/pkg/util/genutil" "github.com/OpenIMSDK/protocol/constant" "github.com/OpenIMSDK/protocol/msg" "github.com/OpenIMSDK/protocol/sdkws" "github.com/OpenIMSDK/tools/discoveryregistry" - "github.com/OpenIMSDK/tools/errs" "github.com/OpenIMSDK/tools/log" "github.com/OpenIMSDK/tools/utils" "github.com/openimsdk/open-im-server/v3/pkg/common/config" @@ -135,7 +136,7 @@ type Message struct { func NewMessage(discov discoveryregistry.SvcDiscoveryRegistry, config *config.GlobalConfig) *Message { conn, err := discov.GetConn(context.Background(), config.RpcRegisterName.OpenImMsgName) if err != nil { - panic(err) + util.ExitWithError(err) } client := msg.NewMsgClient(conn) return &Message{discov: discov, conn: conn, Client: client, Config: config} diff --git a/tools/up35/pkg/pkg.go b/tools/up35/pkg/pkg.go index 49f471204..54aef1ce9 100644 --- a/tools/up35/pkg/pkg.go +++ b/tools/up35/pkg/pkg.go @@ -161,7 +161,7 @@ func NewTask[A interface{ TableName() string }, B any, C any](gormDB *gorm.DB, m tableName := zero.TableName() coll, err := getColl(obj) if err != nil { - return fmt.Errorf("get mongo collection %s failed, err: %w", tableName, err) + return errs.Wrap(fmt.Errorf("get mongo collection %s failed, err: %w", tableName, err)) } var count int defer func() { @@ -174,7 +174,7 @@ func NewTask[A interface{ TableName() string }, B any, C any](gormDB *gorm.DB, m if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == 1146 { return nil // table not exist } - return fmt.Errorf("find mysql table %s failed, err: %w", tableName, err) + return errs.Wrap(fmt.Errorf("find mysql table %s failed, err: %w", tableName, err)) } if len(res) == 0 { return nil @@ -184,7 +184,7 @@ func NewTask[A interface{ TableName() string }, B any, C any](gormDB *gorm.DB, m temp[i] = convert(res[i]) } if err := insertMany(coll, temp); err != nil { - return fmt.Errorf("insert mongo table %s failed, err: %w", tableName, err) + return errs.Wrap(fmt.Errorf("insert mongo table %s failed, err: %w", tableName, err)) } count += len(res) if len(res) < batch { @@ -197,7 +197,7 @@ func NewTask[A interface{ TableName() string }, B any, C any](gormDB *gorm.DB, m func insertMany(coll *mongo.Collection, objs []any) error { if _, err := coll.InsertMany(context.Background(), objs); err != nil { if !mongo.IsDuplicateKeyError(err) { - return err + return errs.Wrap(err) } } for i := range objs { From c79f46f8a34227dca35fc3dd8ea041705b038b43 Mon Sep 17 00:00:00 2001 From: skiffer-git <72860476+skiffer-git@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:29:54 +0800 Subject: [PATCH 72/74] Optimize logs (#2007) * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * fix: wrap the error of group user and thrid (#2005) * fix: wrap the error of group user and thrid * fix: del the chinese comment * fix: fix the make_lint error * fix: fix the ApiTest error * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs * Optimize script logs --------- Co-authored-by: Brabem <69128477+luhaoling@users.noreply.github.com> --- scripts/install/openim-api.sh | 4 +- scripts/install/openim-crontask.sh | 5 +- scripts/install/openim-msggateway.sh | 4 +- scripts/install/openim-msgtransfer.sh | 5 +- scripts/install/openim-push.sh | 4 +- scripts/install/openim-rpc.sh | 3 +- scripts/lib/logging.sh | 7 +- scripts/lib/util.sh | 95 ++++++++++++++------------- scripts/start-all.sh | 8 +-- 9 files changed, 73 insertions(+), 62 deletions(-) diff --git a/scripts/install/openim-api.sh b/scripts/install/openim-api.sh index 8a7b8d1f1..cd3d5eb08 100755 --- a/scripts/install/openim-api.sh +++ b/scripts/install/openim-api.sh @@ -88,8 +88,8 @@ function openim::api::start_service() { echo "Starting service with command: $cmd" - nohup $cmd >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & - + #nohup $cmd >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & if [ $? -ne 0 ]; then openim::log::error_exit "Failed to start ${binary_name} on port ${service_port}." return 1 diff --git a/scripts/install/openim-crontask.sh b/scripts/install/openim-crontask.sh index ae78a44dd..191701082 100755 --- a/scripts/install/openim-crontask.sh +++ b/scripts/install/openim-crontask.sh @@ -51,8 +51,9 @@ function openim::crontask::start() { openim::log::status "Start OpenIM Cron, path: ${OPENIM_CRONTASK_BINARY}" openim::log::status "start cron_task process, path: ${OPENIM_CRONTASK_BINARY}" - - nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + #nohup ${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + cmd="${OPENIM_CRONTASK_BINARY} -c ${OPENIM_PUSH_CONFIG}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & return 0 } diff --git a/scripts/install/openim-msggateway.sh b/scripts/install/openim-msggateway.sh index 3eb9bb349..25051aa6e 100755 --- a/scripts/install/openim-msggateway.sh +++ b/scripts/install/openim-msggateway.sh @@ -61,8 +61,10 @@ function openim::msggateway::start() { if [[ -n "${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" ]]; then PROMETHEUS_PORT_OPTION="--prometheus_port ${MSG_GATEWAY_PROM_PORTS_ARRAY[$i]}" fi + cmd="${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + # nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & - nohup ${OPENIM_MSGGATEWAY_BINARY} --port ${OPENIM_MSGGATEWAY_PORTS_ARRAY[$i]} --ws_port ${OPENIM_WS_PORTS_ARRAY[$i]} $PROMETHEUS_PORT_OPTION -c ${OPENIM_MSGGATEWAY_CONFIG} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done return 0 } diff --git a/scripts/install/openim-msgtransfer.sh b/scripts/install/openim-msgtransfer.sh index 9fdf07fe2..23ce79c36 100755 --- a/scripts/install/openim-msgtransfer.sh +++ b/scripts/install/openim-msgtransfer.sh @@ -57,10 +57,11 @@ function openim::msgtransfer::start() { openim::util::stop_services_on_ports ${PROMETHEUS_MSG_TRANSFER_PORT} PROMETHEUS_PORT_OPTION="--prometheus_port ${PROMETHEUS_MSG_TRANSFER_PORT}" fi - nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + cmd="${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + #nohup ${OPENIM_MSGTRANSFER_BINARY} ${PROMETHEUS_PORT_OPTION} -c ${OPENIM_MSGTRANSFER_CONFIG} -n ${i} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done return 0 - } function openim::msgtransfer::check() { diff --git a/scripts/install/openim-push.sh b/scripts/install/openim-push.sh index 8684f185c..8dea4b1f1 100755 --- a/scripts/install/openim-push.sh +++ b/scripts/install/openim-push.sh @@ -71,7 +71,9 @@ function openim::push::start() { for (( i=0; i<${#OPENIM_PUSH_PORTS_ARRAY[@]}; i++ )); do openim::log::info "start push process, port: ${OPENIM_PUSH_PORTS_ARRAY[$i]}, prometheus port: ${PUSH_PROM_PORTS_ARRAY[$i]}" - nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + cmd="${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]}" + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & + #nohup ${OPENIM_PUSH_BINARY} --port ${OPENIM_PUSH_PORTS_ARRAY[$i]} -c ${OPENIM_PUSH_CONFIG} --prometheus_port ${PUSH_PROM_PORTS_ARRAY[$i]} >> ${LOG_FILE} 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & done return 0 diff --git a/scripts/install/openim-rpc.sh b/scripts/install/openim-rpc.sh index e7ee430d2..c3ac12449 100755 --- a/scripts/install/openim-rpc.sh +++ b/scripts/install/openim-rpc.sh @@ -165,7 +165,8 @@ function openim::rpc::start_service() { printf "Specifying prometheus port: %s\n" "${prometheus_port}" cmd="${cmd} --prometheus_port ${prometheus_port}" fi - nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + #nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "${STDERR_LOG_FILE}" "$TMP_LOG_FILE" >&2) & + nohup ${cmd} >> "${LOG_FILE}" 2> >(tee -a "$TMP_LOG_FILE" | while read line; do echo -e "\e[31m${line}\e[0m"; done >&2) >/dev/null & return 0 } diff --git a/scripts/lib/logging.sh b/scripts/lib/logging.sh index bef3b5961..8d017b077 100755 --- a/scripts/lib/logging.sh +++ b/scripts/lib/logging.sh @@ -237,4 +237,9 @@ function openim::log::test_log() { openim::log::error_exit "openim::log::error_exit" } -# openim::log::test_log \ No newline at end of file +# openim::log::test_log + +function openim::log::print_blue() { + echo -e "\033[0;36m$1\033[0m" +} + diff --git a/scripts/lib/util.sh b/scripts/lib/util.sh index 4322255bb..d84562c1d 100755 --- a/scripts/lib/util.sh +++ b/scripts/lib/util.sh @@ -401,31 +401,32 @@ openim::util::check_process_names() { # Arrays to collect details of processes local not_started=() local started=() - - # Iterate over each given process name + + # Iterate over each given process name for process_name in "$@"; do - # Use `pgrep` to find process IDs related to the given process name - local pids=($(pgrep -f $process_name)) - - # Check if any process IDs were found - if [[ ${#pids[@]} -eq 0 ]]; then - not_started+=($process_name) - else - # If there are PIDs, loop through each one - for pid in "${pids[@]}"; do - local command=$(ps -p $pid -o cmd=) - local start_time=$(ps -p $pid -o lstart=) - local port=$(get_port $pid) - - # Check if port information was found for the PID - if [[ -z $port ]]; then - port="N/A" - fi - - started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") - done - fi - done + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o cmd=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done + # Print information if [[ ${#not_started[@]} -ne 0 ]]; then @@ -1679,29 +1680,29 @@ openim::util::check_process_names() { echo "Checking processes: $*" # Iterate over each given process name - for process_name in "$@"; do - # Use `pgrep` to find process IDs related to the given process name - local pids=($(pgrep -f $process_name)) - - # Check if any process IDs were found - if [[ ${#pids[@]} -eq 0 ]]; then - not_started+=($process_name) - else - # If there are PIDs, loop through each one - for pid in "${pids[@]}"; do - local command=$(ps -p $pid -o cmd=) - local start_time=$(ps -p $pid -o lstart=) - local port=$(get_port $pid) - - # Check if port information was found for the PID - if [[ -z $port ]]; then - port="N/A" - fi - - started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") - done - fi - done + for process_name in "$@"; do + # Use `pgrep` to find process IDs related to the given process name + local pids=($(pgrep -f $process_name)) + + # Check if any process IDs were found + if [[ ${#pids[@]} -eq 0 ]]; then + not_started+=($process_name) + else + # If there are PIDs, loop through each one + for pid in "${pids[@]}"; do + local command=$(ps -p $pid -o cmd=) + local start_time=$(ps -p $pid -o lstart=) + local port=$(get_port $pid) + + # Check if port information was found for the PID + if [[ -z $port ]]; then + port="N/A" + fi + + started+=("Process $process_name - Command: $command, PID: $pid, Port: $port, Start time: $start_time") + done + fi + done # Print information if [[ ${#not_started[@]} -ne 0 ]]; then diff --git a/scripts/start-all.sh b/scripts/start-all.sh index 9a98bfca3..c27f2010c 100755 --- a/scripts/start-all.sh +++ b/scripts/start-all.sh @@ -37,7 +37,7 @@ function execute_start_scripts() { # Check if the script file exists and is executable. if [[ -x "$script_path" ]]; then - openim::log::info "Starting script: ${script_path##*/}" # Log the script name. + openim::log::print_blue "Starting script: ${script_path##*/}" # Log the script name. # Execute the script with the constructed argument. result=$("$script_path" "$arg") @@ -69,7 +69,7 @@ fi "${OPENIM_ROOT}"/scripts/init-config.sh --skip -echo "You need to start the following scripts in order: ${OPENIM_SERVER_SCRIPTARIES[@]}" +#openim::log::print_blue "Execute the following script in sequence: ${OPENIM_SERVER_SCRIPTARIES[@]}" # TODO Prelaunch tools, simple for now, can abstract functions later @@ -81,7 +81,7 @@ ${TOOLS_START_SCRIPTS_PATH} openim::tools::pre-start result=$("${OPENIM_ROOT}"/scripts/stop-all.sh) if [[ $? -ne 0 ]]; then - echo "+++ cat openim log file >>> ${LOG_FILE}" + openim::log::error "View the error logs from this startup. ${LOG_FILE} \n" openim::log::error "Some programs have not exited; the start process is aborted .\n $result" exit 1 fi @@ -95,7 +95,6 @@ sleep 2 result=$(. $(dirname ${BASH_SOURCE})/install/openim-msgtransfer.sh openim::msgtransfer::check) if [[ $? -ne 0 ]]; then - echo "+++ cat openim log file >>> ${LOG_FILE}" openim::log::error "The program may fail to start.\n $result" exit 1 fi @@ -103,7 +102,6 @@ fi result=$(openim::util::check_process_names ${OPENIM_ALL_SERVICE_LIBRARIES_NO_TRANSFER[@]}) if [[ $? -ne 0 ]]; then - echo "+++ cat openim log file >>> ${LOG_FILE}" openim::log::error "The program may fail to start.\n $result" exit 1 fi From 52ddc4f05bce1a9ef0eca2c61e8cb18be9cd4fd3 Mon Sep 17 00:00:00 2001 From: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Wed, 6 Mar 2024 19:04:31 +0800 Subject: [PATCH 73/74] fix: remove unnecessary error handling to avoid sending failed notifications. (#2009) --- pkg/rpcclient/notification/group.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/rpcclient/notification/group.go b/pkg/rpcclient/notification/group.go index 5fd794be2..c72aa839b 100644 --- a/pkg/rpcclient/notification/group.go +++ b/pkg/rpcclient/notification/group.go @@ -250,9 +250,6 @@ func (g *GroupNotificationSender) fillOpUser(ctx context.Context, opUser **sdkws if opUser == nil { return errs.ErrInternalServer.Wrap("**sdkws.GroupMemberFullInfo is nil") } - if *opUser != nil { - return errs.ErrArgs.Wrap("*opUser is not nil") - } userID := mcontext.GetOpUserID(ctx) if groupID != "" { if authverify.IsManagerUserID(userID, g.config) { From 13d0883507be9fa915b80c9ca2ff5d7cda4511b4 Mon Sep 17 00:00:00 2001 From: OpenIM-Gordon <46924906+FGadvancer@users.noreply.github.com> Date: Wed, 6 Mar 2024 20:53:36 +0800 Subject: [PATCH 74/74] fix: remove unnecessary error handling to avoid sending nil resp. (#2010) * fix: remove unnecessary error handling to avoid sending failed notifications. * fix: remove unnecessary error handling to avoid sending nil resp. --- internal/rpc/group/group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/rpc/group/group.go b/internal/rpc/group/group.go index d44c79b45..4be8fab0c 100644 --- a/internal/rpc/group/group.go +++ b/internal/rpc/group/group.go @@ -757,7 +757,7 @@ func (s *groupServer) GroupApplicationResponse(ctx context.Context, req *pbgroup return nil, errs.ErrGroupRequestHandled.Wrap("group request already processed") } var inGroup bool - if _, takeErr := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); takeErr == nil { + if _, err := s.db.TakeGroupMember(ctx, req.GroupID, req.FromUserID); err == nil { inGroup = true // Already in group } else if !s.IsNotFound(err) { return nil, err