diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 074ec2041e6ec6e9900274746712488c8e078ea1..97f916458c5d28a6b8ef56af05c1a96743a6b64d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -26,14 +26,14 @@ cache:
     
 
 stages:
+  - build-df-cli
+  - deploy-meilisearch
   - zotero
   - get-meili-key
   - build
   # - build-wiki
   - delete-release
-  - build-df-cli
   - lint
-  - deploy-meilisearch
   - update-meilisearch-indexes
   - deploy
   - post-deploy
@@ -236,6 +236,21 @@ lint:
       update
       --file data/list-systems.json
       --document systems
+    - > 
+      df-wiki-cli
+      meilisearch 
+      --host ${MEILI_HOST} 
+      --key ${MEILI_MASTER_KEY}
+      delete-all-documents article
+    - >
+      df-wiki-cli
+      meilisearch 
+      --host ${MEILI_HOST} 
+      --key ${MEILI_MASTER_KEY}
+      update
+      --file zot-articles.json
+      --document article
+
 
 
   allow_failure: false
@@ -293,28 +308,17 @@ set-meili-env:prod:
     - if: $CI_COMMIT_BRANCH == "main"  
 
 ##############################
-get-zotero:
+sync-zotero:
   extends: .df-wiki-cli-run
   stage: zotero
   script:
-    - df-wiki-cli articles --key ${ZOTERO_API_KEY} --output content/_data/_articles.json
+    - df-wiki-cli articles missing-doi --dir ./content/ --key ${ZOTERO_API_KEY}
+    - df-wiki-cli articles fetch-from-zotero --key ${ZOTERO_API_KEY} --output zot-articles.json
   artifacts:
     paths:
-      - content/_data/_articles.json
+      - zot-articles.json
   rules:
-    - if: $CI_COMMIT_BRANCH == "main"
-
-# get-pfam:
-#   extends: .df-wiki-cli-run
-#   stage: get-data
-#   script:
-#     - df-wiki-cli pfam --output public/pfam-a-hmm.csv
-#   artifacts:
-#     paths:
-#       - public/pfam-a-hmm.csv
-  # rules:
-  #   - if: $CI_COMMIT_BRANCH == "main"
-
+    - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "rework-references"
 
 
 ################ BUILD ##########################
@@ -359,7 +363,7 @@ build:prod:wiki:
   extends: .build
   needs:
     - set-meili-env:prod
-    - get-zotero
+    - sync-zotero
     # - get-pfam
   variables:
     BASE_URL: /wiki/
@@ -405,7 +409,7 @@ build:prod:wiki:
 deploy:dev:
   extends: .deploy
   rules:
-    - if: $CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "refseq-no-sys"
+    - if: $CI_COMMIT_BRANCH == "dev" || $CI_COMMIT_BRANCH == "rework-references"
   needs:
     - "build:dev:wiki"
   when: manual
@@ -471,7 +475,7 @@ delete-helm-release:prod:
 
 
 
-.post-deploy:
+.create-structures-archives:
   stage: post-deploy
   image: harbor.pasteur.fr/kube-system/helm-kubectl:$HELM_VERSION
   when: manual
@@ -488,8 +492,8 @@ delete-helm-release:prod:
     - kubectl --namespace ${KUBE_NAMESPACE} exec ${WIKI_POD} -- bash -c "cd /usr/share/nginx/html/ && find ./ -name '*.cif' -exec tar -czvf /usr/share/nginx/html/df-all-cifs.tar.gz {} +"
 
 
-post-deploy:dev:
-  extends: .post-deploy
+create-structures-archives:dev:
+  extends: .create-structures-archives
   rules:
     - if: $CI_COMMIT_BRANCH != "main"  
   needs:
@@ -505,8 +509,8 @@ post-deploy:dev:
     url: "https://${HOST_DEV}"
 
 
-post-deploy:prod:
-  extends: .post-deploy
+create-structures-archives:prod:
+  extends: .create-structures-archives
   rules:
     - if: $CI_COMMIT_BRANCH == "main"  
   needs:
diff --git a/components/content/ArticleDoi.vue b/components/content/ArticleDoi.vue
index fa7e1b86ef58c6e14c2bcb72738255237108d015..271556aec9e788ac11bdf1d0debe114434461aac 100644
--- a/components/content/ArticleDoi.vue
+++ b/components/content/ArticleDoi.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { useDisplay } from "vuetify";
-import { useArticlesStore } from '@/stores/articles'
+import type { WikiArticle } from '@/types/articles';
 
 export interface Props {
   index?: number;
@@ -19,10 +19,20 @@ const props = withDefaults(defineProps<Props>(), {
   isRelevant: false,
 });
 
-const { article } = useFetchArticle(props.doi);
+const article = ref<WikiArticle | undefined>(undefined)
 const { mobile } = useDisplay();
 const show = ref(false);
 
+onBeforeMount(async () => {
+  const { article: articleOnMounted } = await useFetchArticle(props.doi);
+  article.value = articleOnMounted.value
+})
+
+
+onMounted(async () => {
+  const { article: articleOnMounted } = await useFetchArticle(props.doi);
+  article.value = articleOnMounted.value
+})
 
 const articleTitle = computed(() => {
   return props?.title ?? article?.value?.title ?? props.doi;
@@ -32,16 +42,11 @@ const articleAbstract = computed(() => {
 });
 </script>
 <template>
-  <v-list-item :href="article?.href" :id="`ref-${props.doi}`" :target="article?.target" density="compact" color="transparent"
-    class="px-1">
+  <v-list-item :href="article?.href" :id="`ref-${props.doi}`" :target="article?.target" density="compact"
+    color="transparent" class="px-1">
     <template v-if="!mobile" #prepend>
       <v-icon icon="md:star" :color="props.isRelevant ? 'info' : 'transparent'"></v-icon>
     </template>
-    <!-- <template v-if="!mobile" #append>
-      <v-btn v-if="articleAbstract" size="x-small" variant="plain"
-        :append-icon="show ? 'mdi-chevron-up' : 'mdi-chevron-down'" class="px-0"
-        @click.stop.prevent="show = !show">Abstract</v-btn>
-    </template> -->
     <v-card flat color="transparent" density="compact" class="my-0 article-ref">
       <v-card-item density="compact" class="pa-0">
         <v-toolbar class="py-0 d-flex align-start article-toolbar" color="transparent" :height="20">
@@ -51,9 +56,6 @@ const articleAbstract = computed(() => {
             :append-icon="show ? 'mdi-chevron-up' : 'mdi-chevron-down'" class="px-1 align-center"
             @click.stop.prevent="show = !show">Abstract</v-btn>
         </v-toolbar>
-        <!-- <v-card-title class="py-0"><span class="font-weight-bold">{{
-            articleTitle
-          }}</span></v-card-title> -->
         <v-card-subtitle class="py-0">
           {{ article?.subtitle ?? "no authors" }}</v-card-subtitle>
         <v-card-subtitle class="py-0">
diff --git a/components/content/ArticleReference.vue b/components/content/ArticleReference.vue
deleted file mode 100644
index 4c18ab75841fb8a95fffc3e4339220678fd32aba..0000000000000000000000000000000000000000
--- a/components/content/ArticleReference.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script setup lang="ts">
-import { useDisplay } from "vuetify";
-
-export interface Props {
-    index?: number;
-    doi: string;
-    divider?: boolean;
-    enumerate?: boolean;
-    title?: string;
-    abstract?: string;
-}
-
-const props = withDefaults(defineProps<Props>(), {
-    enumerate: true,
-    divider: false,
-});
-const { article } = useFetchArticle(props.doi);
-const { mobile } = useDisplay();
-
-const articleTitle = computed(() => {
-    return props?.title ?? article?.value?.title ?? props.doi;
-});
-
-const articleAuthorsString = computed(() => {
-    // console.log(article.value)
-    // console.log(props.doi)
-    return article.value?.author?.length > 0 ? `${article.value.author[0].family} & al` : null
-})
-
-</script>
-<template>
-    <v-list-item v-if="article" :href="article?.href" :id="`ref-${props.doi}`" :target="article?.target" density="compact"
-        class="px-1">
-        <template #prepend v-if="!mobile && enumerate">
-            <v-avatar color="primary" size="small" density="compact" variant="tonal">
-                {{ props?.index ?? "#" }}
-            </v-avatar>
-        </template>
-
-        <template #title>
-            <span class="text-subtitle-1 font-weight-bold">{{
-                articleTitle
-            }} </span>
-
-        </template>
-        <template #subtitle><span class="text-caption">{{ articleAuthorsString }}</span></template>
-        <template #append v-if="!mobile">
-            <span> {{ article?.containerTitle ?? "no containerTitle" }} ({{
-                article?.year
-            }})</span>
-        </template>
-
-    </v-list-item>
-    <v-divider v-if="props.divider" inset></v-divider>
-</template>
\ No newline at end of file
diff --git a/components/content/RefArticle.vue b/components/content/RefArticle.vue
index 4242f444d04dc76146a45053479c1d6d0180dd9c..ee9181ccb05925b5b1e53c2a7a80c70e9c218e87 100644
--- a/components/content/RefArticle.vue
+++ b/components/content/RefArticle.vue
@@ -1,12 +1,23 @@
 <script setup lang="ts">
 import { useTheme } from "vuetify";
+import type { WikiArticle } from '@/types/articles';
+
+
 const theme = useTheme();
 export interface Props {
     doi: string;
 }
 const props = withDefaults(defineProps<Props>(), {});
-
-const { article } = useFetchArticle(props.doi);
+const article = ref<WikiArticle | undefined>(undefined)
+// const { article } = useFetchArticle(props.doi);
+onBeforeMount(async () => {
+    const { article: articleOnMounted } = await useFetchArticle(props.doi);
+    article.value = articleOnMounted.value
+})
+onMounted(async () => {
+    const { article: articleOnMounted } = await useFetchArticle(props.doi);
+    article.value = articleOnMounted.value
+})
 </script>
 <template>
     <!-- <v-chip v-if="article" variant="text" :href="`#ref-${props.doi}`" class="pa-0 font-italic">{{
diff --git a/composables/useFetchArticle.ts b/composables/useFetchArticle.ts
index 59c1e51851cd53369ccd27d4db85c17b86508d28..eef82b70629bd8487f6c421e0537733cf7068628 100644
--- a/composables/useFetchArticle.ts
+++ b/composables/useFetchArticle.ts
@@ -1,60 +1,22 @@
-import { useArticlesStore, type CslJson } from '../stores/articles'
-import { ref, computed, watchEffect, toValue } from "vue"
-// import { useFetch } from '#app';
-// import { useFetch } from "nuxt"
-import { type MaybeRef, useFetch } from '#imports'
-import article from "@/public/articles.json"
+import { ref, toValue } from "vue"
+import { StorageSerializers } from "@vueuse/core"
+import type { CslJson, WikiArticle } from '@/types/articles';
+import type { SearchParams } from 'meilisearch'
 
-export interface CrossrefArticle {
-    DOI: string;
-    issue: number;
-    type: string;
-    title: string[];
-    author: Array<{ family: string; given: string }>;
-    // "container-title-short": string;
-    "short-container-title": string;
-    "container-title": string;
-    abstract?: string;
-    published: {
-        "date-parts": string[];
-    };
-    issued: {
-        "date-parts": string[];
-    };
-}
-
-
-
-export interface WikiArticle {
-    DOI: string
-    title: string
-    subtitle: string
-    author: Array<{ family: string; given: string }>
-    containerTitle: string
-    abstract: string
-    year: string
-    href: string
-    target: string
-    prependIcon: string
-}
-export interface RawArticle {
-    message: CrossrefArticle
-
-}
-
-type SrcArticle = CrossrefArticle | CslJson
-
-export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
-    // const article = ref<Article>()
-    // const rawArticle = ref<RawArticle>()
-    const srcArticle = ref<SrcArticle | null>(null)
-    const store = useArticlesStore()
-    const pending = ref(false)
+export async function useFetchArticle(doi: string = "") {
     const doiBaseUrl = ref(new URL("https://doi.org/"));
-    const url = ref(new URL(`/works/${toValue(doi)}`, " https://api.crossref.org/").href);
-    const article = ref()
-    const zoteroArticles = ref()
-
+    // const article = ref<WikiArticle | undefined>(undefined)
+    const article = useSessionStorage<WikiArticle>(doi, null, { serializer: StorageSerializers.object })
+    const client = useMeiliSearchRef()
+    const index = ref("article")
+    const params = ref<SearchParams>({
+        facets: ["*"],
+        filter: [`DOI='${doi}'`],
+        limit: 1
+    })
+    function getReferenceUrl(doi: string) {
+        return new URL(doi, doiBaseUrl.value).href;
+    }
     function toAuthorsString(authors: Array<{ family: string; given: string }>) {
         return authors
             .map((curr) => {
@@ -62,12 +24,7 @@ export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
             })
             .join(", ");
     }
-
-    function getReferenceUrl(doi: string) {
-        return new URL(doi, doiBaseUrl.value).href;
-    }
-
-    function zoteroArticleToArticle(zoteroArticle: CslJson) {
+    function zoteroArticleToArticle(zoteroArticle: CslJson): WikiArticle | undefined {
         if (zoteroArticle != undefined) {
             const {
                 DOI,
@@ -90,74 +47,38 @@ export function useFetchArticle(doi: MaybeRef<string> = ref("")) {
                 target: "_blank",
                 prependIcon: "mdi-newspaper-variant-outline",
             }
-        }
+        } else { return undefined }
+    }
 
+    if (!article.value) {
+        const { data, error } = await useAsyncData(
+            doi,
+            async () => {
+                return await client.index(toValue(index)).search<CslJson>("", toValue(params))
+            },
+            {
+                transform: function (data) {
+                    if (data !== undefined && data?.hits.length >= 1) {
+                        return zoteroArticleToArticle(data.hits[0])
+                    }
+                }
+            }
+        )
 
-    }
-    function crossrefToArticle(article: CrossrefArticle): WikiArticle {
-        const { title, DOI, type, "container-title": ct, "short-container-title": sct, abstract, author, issued } = article
-        // let sanitizedAbstract = abstract
-        const sanitizedAbstract = abstract ? /(?:\<jats\:p\>)?(.*)(?:\<\/jats\:p\>)?/.exec(abstract)?.[1] ?? '' : ''
-        const sanitizedContainerTitle = sct?.length > 0 ? sct[0] : ct?.length > 0 ? ct[0] : ""
-        return {
-            title: title?.length > 0 ? title[0] : "",
-            DOI,
-            abstract: sanitizedAbstract,
-            containerTitle: sanitizedContainerTitle,
-            subtitle: toAuthorsString(author || []),
-            author,
-            year: issued?.["date-parts"][0][0] ?? '',
-            href: getReferenceUrl(DOI),
-            target: "_blank",
-            prependIcon: "mdi-newspaper-variant-outline"
+        if (error.value) {
+            throw createError({
+                ...error.value,
+                statusMessage: `Could not fetch article ${doi}`
+            })
         }
+        article.value = data.value
     }
+    return { article }
+}
 
-    function crossrefToCsl(article: CrossrefArticle): CslJson {
-        const { title, DOI, type, "container-title": ct, "short-container-title": sct, abstract, author, issued } = article
-        // let sanitizedAbstract = abstract
-        const sanitizedAbstract = abstract ? /(?:\<jats\:p\>)?(.*)(?:\<\/jats\:p\>)?/.exec(abstract)?.[1] ?? '' : ''
-        const sanitizedContainerTitle = sct?.length > 0 ? sct[0] : ct?.length > 0 ? ct[0] : ""
-        return {
-            title: title?.length > 0 ? title[0] : "",
-            type,
-            DOI,
-            abstract: sanitizedAbstract,
-            author,
-            "container-title": sanitizedContainerTitle,
-            issued
 
-        }
-    }
 
-    if (store.articles.has(toValue(doi))) {
-        const cslArticle = store.articles.get(toValue(doi))
-        article.value = cslArticle ? zoteroArticleToArticle(cslArticle) : undefined
-    }
-    else {
-        useFetch<RawArticle>(toValue(url), {
-            lazy: true, server: false,
-        }).then(({ data, pending: pendingUseFetch }) => {
-            if (data.value?.message) {
-                article.value = crossrefToArticle(data.value.message)
-                store.add(crossrefToCsl(data.value.message))
-            }
-            pending.value = pendingUseFetch.value
-        })
-    }
-    // const fetchCrossRef = () => {
-    //     useFetch<RawArticle>(toValue(url), {
-    //         lazy: true, server: false,
-    //     }).then(({ data, pending: pendingUseFetch }) => {
-    //         if (data.value?.message) {
-    //             srcArticle.value = data.value.message
-    //         }
-    //         pending.value = pendingUseFetch.value
-    //     })
 
-    // }
 
-    return { article, pending }
-}
 
 
diff --git a/content/3.defense-systems/paris.md b/content/3.defense-systems/paris.md
index 7aaafe5058f11deef029c573d18a4118bfa2909d..a09b72fca555cde404bc032bccbf8a9fd83f9ce0 100644
--- a/content/3.defense-systems/paris.md
+++ b/content/3.defense-systems/paris.md
@@ -23,7 +23,7 @@ PARIS (for Phage Anti-Restriction-Induced System) is a novel anti-phage system.
 
 ## Molecular mechanisms
 
-This system relies on an unknown [Abortive infection](/general-concepts/abortive-infection) mechanism to trigger growth arrest upon sensing a phage-encoded protein (Ocr). Interestingly, the Ocr protein has been found to inhibit R-M systems and BREX systems, making PARIS a suitable defense mechanism against RM resistant and/or BREX resistant phages :ref{doi=0.1016/j.chom.2022.02.018,10.1093/nar/gkaa510,10.1007/BF01036001}. 
+This system relies on an unknown [Abortive infection](/general-concepts/abortive-infection) mechanism to trigger growth arrest upon sensing a phage-encoded protein (Ocr). Interestingly, the Ocr protein has been found to inhibit R-M systems and BREX systems, making PARIS a suitable defense mechanism against RM resistant and/or BREX resistant phages :ref{doi=10.1016/j.chom.2022.02.018,10.1093/nar/gkaa510,10.1007/BF01036001}. 
 
 ## Example of genomic structure
 
diff --git a/packages/df-wiki-cli/df_wiki_cli/articles/__init__.py b/packages/df-wiki-cli/df_wiki_cli/articles/__init__.py
index 123615c1548e058679701b7ced37c10c0e567b34..7e90a068d8307dd3dee85e7616351191b9df27ad 100644
--- a/packages/df-wiki-cli/df_wiki_cli/articles/__init__.py
+++ b/packages/df-wiki-cli/df_wiki_cli/articles/__init__.py
@@ -1,9 +1,15 @@
 import json
 from pyzotero import zotero
 from pathlib import Path
+from habanero import Crossref
+from rich.console import Console
 
+console = Console()
 
-def fetch_articles(key: str, batch_size: int = 100, output: Path = "articles.json"):
+
+def fetch_articles_from_zotero(
+    key: str, batch_size: int = 100, output: Path = "articles.json"
+):
     zot = zotero.Zotero("5151022", "group", key)
     collection = zot.collection("BSWL96X3")
     tot_items = collection["meta"]["numItems"]
@@ -16,7 +22,7 @@ def fetch_articles(key: str, batch_size: int = 100, output: Path = "articles.jso
                 format="csljson",
                 limit=batch_size,
                 start=i,
-                itemType="journalArticle",
+                itemType="journalArticle || Preprint",
                 sort="title",
             )["items"]
             for item in items:
@@ -26,3 +32,118 @@ def fetch_articles(key: str, batch_size: int = 100, output: Path = "articles.jso
     json_object = json.dumps(items, indent=2)
     with open(output, "w") as outfile:
         outfile.write(json_object)
+
+
+def add_articles_to_zotero_from_doi(doi, key):
+    cr = Crossref(mailto="defense-finder@pasteur.fr")
+    res = cr.works(ids=[doi])
+
+    zot = zotero.Zotero("5151022", "group", key)
+
+    # pyzotero grabs the template dict from the server
+
+    # console.print(zitem)
+    message = res["message"]
+    console.print(f"add doi {doi}, type {message['type']}")
+    if message["type"] == "posted-content":
+        # {
+        #     "itemType": "Preprint",
+        #     "title": "",
+        #     "creators": [{"creatorType": "author", "firstName": "", "lastName": ""}],
+        #     "abstractNote": "",
+        #     "genre": "",
+        #     "repository": "",
+        #     "archiveID": "",
+        #     "place": "",
+        #     "date": "",
+        #     "series": "",
+        #     "seriesNumber": "",
+        #     "DOI": "",
+        #     "citationKey": "",
+        #     "url": "",
+        #     "accessDate": "",
+        #     "archive": "",
+        #     "archiveLocation": "",
+        #     "shortTitle": "",
+        #     "language": "",
+        #     "libraryCatalog": "",
+        #     "callNumber": "",
+        #     "rights": "",
+        #     "extra": "",
+        #     "tags": [],
+        #     "collections": [],
+        #     "relations": {},
+        # }
+        itemtype = "Preprint"
+    elif message["type"] == "journal-article":
+        #     {
+        #     'itemType': 'journalArticle',
+        #     'title': '',
+        #     'creators': [{'creatorType': 'author', 'firstName': '', 'lastName': ''}],
+        #     'abstractNote': '',
+        #     'publicationTitle': '',
+        #     'volume': '',
+        #     'issue': '',
+        #     'pages': '',
+        #     'date': '',
+        #     'series': '',
+        #     'seriesTitle': '',
+        #     'seriesText': '',
+        #     'journalAbbreviation': '',
+        #     'language': '',
+        #     'DOI': '',
+        #     'ISSN': '',
+        #     'shortTitle': '',
+        #     'url': '',
+        #     'accessDate': '',
+        #     'archive': '',
+        #     'archiveLocation': '',
+        #     'libraryCatalog': '',
+        #     'callNumber': '',
+        #     'rights': '',
+        #     'extra': '',
+        #     'tags': [],
+        #     'collections': [],
+        #     'relations': {}
+        # }
+        itemtype = "journalArticle"
+    else:
+        raise NotImplementedError(f"type {message['type']} need to be implemented")
+    zitem = zot.item_template(itemtype)
+    # console.print(message)
+
+    # console.print(message["title"])
+    zitem["title"] = message["title"][0]
+    if "page" in message:
+        zitem["pages"] = message["page"]
+    if "abstract" in message:
+        zitem["abstractNote"] = message["abstract"]
+
+    if "container-title" in message and len(message["container-title"]) > 0:
+        zitem["publicationTitle"] = message["container-title"][0]
+    if "short-container-title" in message and len(message["short-container-title"]) > 0:
+        zitem["journalAbbreviation"] = message["short-container-title"][0]
+    zitem["creators"] = [
+        {
+            "creatorType": "author",
+            "firstName": author["given"],
+            "lastName": author["family"],
+        }
+        for author in message["author"]
+    ]
+    zitem["libraryCatalog"] = "DOI.org (Crossref)"
+    if "ISSN" in message:
+        zitem["ISSN"] = ", ".join(message["ISSN"])
+    zitem["url"] = message["resource"]["primary"]["URL"]
+    zitem["date"] = "/".join([str(d) for d in message["published"]["date-parts"][0]])
+    for key in ["DOI", "volume", "issue", "language"]:
+        if key in message:
+            zitem[key] = message[key]
+
+    items_to_add = [zitem]
+
+    zot.check_items(items_to_add)
+    # res = zot.create_items(items_to_add)
+    # new_item = res["successful"]["0"]
+    # zot.addto_collection("BSWL96X3", new_item)
+    console.print("[green]done")
diff --git a/packages/df-wiki-cli/df_wiki_cli/articles/main.py b/packages/df-wiki-cli/df_wiki_cli/articles/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c4bc2c89d36ee484596680d75325873225f091b
--- /dev/null
+++ b/packages/df-wiki-cli/df_wiki_cli/articles/main.py
@@ -0,0 +1,98 @@
+import typer
+from typing_extensions import Annotated
+from pathlib import Path
+from df_wiki_cli.articles import (
+    add_articles_to_zotero_from_doi,
+    fetch_articles_from_zotero,
+)
+from pyzotero import zotero
+from habanero import Crossref
+from rich.console import Console
+import frontmatter
+import re
+import json
+
+app = typer.Typer()
+console = Console()
+
+
+@app.command()
+def fetch_from_zotero(
+    output: Annotated[
+        Path,
+        typer.Option(
+            exists=False,
+            file_okay=True,
+            writable=True,
+        ),
+    ] = "articles.json",
+    key: Annotated[str, typer.Option(help="Zotero api key")] = "",
+    batch_size: Annotated[
+        int, typer.Option(help="Number articles get per request")
+    ] = 100,
+):
+    """
+    Get articles metadata from Zotero collection
+
+    """
+    if key != "":
+        fetch_articles_from_zotero(key, batch_size, output)
+    else:
+        print("You must provide a zotero api key")
+        raise typer.Exit(code=1)
+
+
+@app.command()
+def missing_doi(
+    dir: Annotated[
+        Path,
+        typer.Option(exists=False, file_okay=False, readable=True, dir_okay=True),
+    ],
+    key: Annotated[str, typer.Option(help="Zotero api key")] = "",
+    batch_size: Annotated[
+        int, typer.Option(help="Number articles get per request")
+    ] = 100,
+):
+    # parse content to look at dois
+
+    # get current list of dois in zotero
+    zotero_list = "/tmp/zotero-dois.json"
+    fetch_articles_from_zotero(key, batch_size, zotero_list)
+    zotero_dois_set = set()
+    with open(zotero_list) as zotero_f:
+        zotero_data = json.load(zotero_f)
+        for d in zotero_data:
+            if "DOI" in d:
+                doi_lower = d["DOI"].lower()
+                zotero_dois_set.add(doi_lower)
+
+    dois_set = set()
+    for file in dir.rglob("*"):
+        if file.suffix == ".md":
+            console.rule(f"[bold blue]{file.name}", style="blue")
+            with open(file) as f:
+                metadata, content = frontmatter.parse(f.read())
+                if (
+                    "relevantAbstracts" in metadata
+                    and len(metadata["relevantAbstracts"]) > 0
+                ):
+                    dois = [d["doi"].lower() for d in metadata["relevantAbstracts"]]
+                    dois_set.update(dois)
+
+                # handle content
+                group = re.findall(r":ref{doi=(.*?)}", content)
+                for g in group:
+                    splitted = [doi.lower() for doi in re.split(",", g)]
+                    dois_set.update(splitted)
+
+    for doi in dois_set - zotero_dois_set:
+        add_articles_to_zotero_from_doi(doi, key)
+
+
+@app.command()
+def from_apicrossref(
+    doi: Annotated[str, typer.Option(help="DOI identifier")],
+    key: Annotated[str, typer.Option(help="Zotero api key")] = "",
+):
+
+    add_articles_to_zotero_from_doi(doi, key)
diff --git a/packages/df-wiki-cli/df_wiki_cli/main.py b/packages/df-wiki-cli/df_wiki_cli/main.py
index ace7d2e92bbc20738e98c10a7a882fe23523a121..c6992782b6e983a434513bf10a08235d7ce0cc56 100644
--- a/packages/df-wiki-cli/df_wiki_cli/main.py
+++ b/packages/df-wiki-cli/df_wiki_cli/main.py
@@ -1,15 +1,16 @@
 import typer
 from pathlib import Path
 from typing_extensions import Annotated
-from df_wiki_cli.articles import fetch_articles
 from df_wiki_cli.pfam import fetch_pfam
 from df_wiki_cli.meilisearch import main as ms_main
 from df_wiki_cli.content import main as content_main
+from df_wiki_cli.articles import main as articles_main
 
 # from df_wiki_cli.ms import main as ms_main
 app = typer.Typer()
 app.add_typer(ms_main.app, name="meilisearch")
 app.add_typer(content_main.app, name="content")
+app.add_typer(articles_main.app, name="articles")
 
 
 @app.callback()
@@ -21,33 +22,6 @@ def callback():
 
     """
 
-
-@app.command()
-def articles(
-    output: Annotated[
-        Path,
-        typer.Option(
-            exists=False,
-            file_okay=True,
-            writable=True,
-        ),
-    ] = "articles.json",
-    key: Annotated[str, typer.Option(help="Zotero api key")] = "",
-    batch_size: Annotated[
-        int, typer.Option(help="Number articles get per request")
-    ] = 100,
-):
-    """
-    Get articles metadata from Zotero collection
-
-    """
-    if key != "":
-        fetch_articles(key, batch_size, output)
-    else:
-        print("You must provide a zotero api key")
-        raise typer.Exit(code=1)
-
-
 @app.command()
 def pfam(
     output: Annotated[
diff --git a/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py b/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py
index 1111225625f07cbc4584ae571646350cbe00a998..5707df46949c4fd609e35340458fb61e1a5c30aa 100644
--- a/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py
+++ b/packages/df-wiki-cli/df_wiki_cli/meilisearch/__init__.py
@@ -308,6 +308,41 @@ def update_systems(
     index.update_typo_tolerance({"enabled": False})
 
 
+def update_articles(
+    host: str,
+    key: str,
+    file: Path,
+    document: str,
+):
+    client = meilisearch.Client(host, key)
+    index = client.index(document.lower())
+    with open(file, "r") as jsonfile:
+        json_object = json.load(jsonfile)
+        for obj in json_object:
+            obj["ms_id"] = obj["id"].replace("/", "_")
+        tasks = index.add_documents_in_batches(json_object, primary_key="ms_id")
+        for task in tasks:
+            print(task)
+
+    pagination_settings_task = index.update_pagination_settings(
+        {"maxTotalHits": 100000}
+    )
+    print(pagination_settings_task)
+    attr_task = index.update_filterable_attributes(
+        body=[
+            "DOI",
+        ]
+    )
+    params = {
+        "maxValuesPerFacet": 1000000,
+        "sortFacetValuesBy": {"*": "count"},
+    }
+    index.update_faceting_settings(params)
+
+    print(attr_task)
+
+
+
 def split_on_comma(str_val: str) -> List[str]:
     for val in str_val.split(","):
         yield val.strip()
diff --git a/packages/df-wiki-cli/df_wiki_cli/meilisearch/main.py b/packages/df-wiki-cli/df_wiki_cli/meilisearch/main.py
index f32ac8c3af221457efe03522b7e0bdf206f0c610..e6cb0e7b9c38fe0874f3efa988810e8494633c9f 100644
--- a/packages/df-wiki-cli/df_wiki_cli/meilisearch/main.py
+++ b/packages/df-wiki-cli/df_wiki_cli/meilisearch/main.py
@@ -3,10 +3,11 @@ import meilisearch
 from typing_extensions import Annotated
 from pathlib import Path
 from df_wiki_cli.meilisearch import (
+    update_refseqtaxo,
+    update_articles,
     update_refseq,
     update_structure,
     update_systems,
-    update_refseqtaxo,
 )
 from enum import Enum
 from types import SimpleNamespace
@@ -23,6 +24,7 @@ class Documents(str, Enum):
     refseq = "refseq"
     structure = "structure"
     systems = "systems"
+    article = "article"
 
 
 @app.callback()
@@ -69,6 +71,8 @@ def update(
         update_structure(ctx.obj.host, ctx.obj.key, file, document)
     if document == "systems":
         update_systems(ctx.obj.host, ctx.obj.key, file, document)
+    if document == "article":
+        update_articles(ctx.obj.host, ctx.obj.key, file, document)
 
 
 @app.command()
diff --git a/packages/df-wiki-cli/poetry.lock b/packages/df-wiki-cli/poetry.lock
index 3b10a841198f28079546217a59f1b62eabd5f82a..8d3b6e0c6dc2126973b96b586c8c0ad2b8250d32 100644
--- a/packages/df-wiki-cli/poetry.lock
+++ b/packages/df-wiki-cli/poetry.lock
@@ -373,6 +373,26 @@ ufo = ["fs (>=2.2.0,<3)"]
 unicode = ["unicodedata2 (>=15.1.0)"]
 woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"]
 
+[[package]]
+name = "habanero"
+version = "1.2.6"
+description = "Low Level Client for Crossref Search API"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "habanero-1.2.6-py2.py3-none-any.whl", hash = "sha256:ccc57e7dc9cc1850961b4e6f85fc7749896cba6ddcc06ea15297dbad9af2b62e"},
+    {file = "habanero-1.2.6.tar.gz", hash = "sha256:b206d49f44f41c2289f0ad731f259a50d4376c747d8ecbb219a73874d45309d4"},
+]
+
+[package.dependencies]
+requests = ">=2.7.0"
+tqdm = "*"
+
+[package.extras]
+all = ["pytest"]
+bibtex = ["bibtexparser (>=2.0.0)"]
+test = ["pytest"]
+
 [[package]]
 name = "idna"
 version = "3.4"
@@ -1297,6 +1317,26 @@ files = [
     {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 ]
 
+[[package]]
+name = "tqdm"
+version = "4.66.2"
+description = "Fast, Extensible Progress Meter"
+optional = false
+python-versions = ">=3.7"
+files = [
+    {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"},
+    {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"},
+]
+
+[package.dependencies]
+colorama = {version = "*", markers = "platform_system == \"Windows\""}
+
+[package.extras]
+dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"]
+notebook = ["ipywidgets (>=6)"]
+slack = ["slack-sdk"]
+telegram = ["requests"]
+
 [[package]]
 name = "typer"
 version = "0.9.0"
@@ -1378,4 +1418,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p
 [metadata]
 lock-version = "2.0"
 python-versions = ">=3.11,<3.13"
-content-hash = "c424b174806be51ccfd8655e9f31c5ac7e1d8a7a362ba99f69746b2e71a59174"
+content-hash = "d526b2f2acfadf5a8eccfae5e902ab702eaedd8ca88ec310d2f6c4dab88bea8a"
diff --git a/packages/df-wiki-cli/pyproject.toml b/packages/df-wiki-cli/pyproject.toml
index 22f5ffe80cd5293c84c985efe6daf7e7e3c715e5..d0a3341443d8102a33b93a44f44f7b16797bb3ed 100644
--- a/packages/df-wiki-cli/pyproject.toml
+++ b/packages/df-wiki-cli/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "df-wiki-cli"
-version = "0.1.5"
+version = "0.1.6"
 description = ""
 authors = ["Remi  PLANEL <rplanel@pasteur.fr>"]
 readme = "README.md"
@@ -18,6 +18,7 @@ pydantic = "^2.4.2"
 pydantic-yaml = "^1.2.0"
 python-frontmatter = "^1.0.1"
 matplotlib = "^3.8.2"
+habanero = "^1.2.6"
 
 
 [tool.poetry.group.dev.dependencies]
diff --git a/stores/articles.ts b/stores/articles.ts
index 1ba95fd2d0584f76814bd6818b4ab794bf0f534a..a28c03bc198d9a37f77cf10e1831765f45fcfc05 100644
--- a/stores/articles.ts
+++ b/stores/articles.ts
@@ -1,24 +1,8 @@
 import { defineStore } from 'pinia'
 import { ref } from 'vue'
 // import jsonArticles from '@/assets/articles.json'
+import type { CslJson } from "@/types/articles"
 
-export interface CslJson {
-    // id: string
-    type: string
-    title: string
-    "container-title": string
-    // page: string,
-    // volume: string,
-    abstract: string
-    // URL: string
-    DOI: string
-    // journalAbbreviation: string
-    // language: string
-    author: Array<{ family: string, given: string }>
-    issued: {
-        "date-parts": Array<string>
-    },
-}
 
 export const useArticlesStore = defineStore('articles', () => {
     const articleMap = new Map([])
diff --git a/types/articles.ts b/types/articles.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92f2f10debb602df6ceac169d3b7cc74b202ebfe
--- /dev/null
+++ b/types/articles.ts
@@ -0,0 +1,55 @@
+export interface WikiArticle {
+    DOI: string
+    title: string
+    subtitle: string
+    author: Array<{ family: string; given: string }>
+    containerTitle: string
+    abstract: string
+    year: string
+    href: string
+    target: string
+    prependIcon: string
+}
+
+export interface CslJson {
+    // id: string
+    type: string
+    title: string
+    "container-title": string
+    // page: string,
+    // volume: string,
+    abstract: string
+    // URL: string
+    DOI: string
+    // journalAbbreviation: string
+    // language: string
+    author: Array<{ family: string, given: string }>
+    issued: {
+        "date-parts": Array<string>
+    },
+}
+
+export interface CrossrefArticle {
+    DOI: string;
+    issue: number;
+    type: string;
+    title: string[];
+    author: Array<{ family: string; given: string }>;
+    // "container-title-short": string;
+    "short-container-title": string;
+    "container-title": string;
+    abstract?: string;
+    published: {
+        "date-parts": string[];
+    };
+    issued: {
+        "date-parts": string[];
+    };
+}
+
+export interface RawArticle {
+    message: CrossrefArticle
+
+}
+
+export type SrcArticle = CrossrefArticle | CslJson