Aller au contenu

#Fichiers

C’est la route qui fait le travail : theme recuperer, theme pousser et theme suivre ne sont que des boucles autour d’elle. Elle lit une arborescence, lit un fichier, en écrit un, en supprime un — un seul à la fois, jamais le thème entier.

Deux onglets ouverts sur deux fichiers différents ne se marchent donc pas dessus, et une sauvegarde interrompue ne laisse pas un thème à moitié réécrit. Pour déplacer un thème complet, c’est Import et export.

L’<id> du chemin est celui d’un thème du site résolu. Un thème d’un autre site répond 404 introuvable, sur les trois méthodes.

#Lire l’arborescence

GET/api/themes/<id>/fichiers
200
json
{
  "theme": {
    "id": "11111111-1111-4111-8111-111111111111",
    "nom": "Origo",
    "role": "publie"
  },
  "fichiers": [
    {
      "chemin": "assets/theme.css",
      "octets": 18422,
      "empreinte": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
      "maj_le": "2026-08-05T09:12:44.108Z"
    },
    {
      "chemin": "config/settings_schema.json",
      "octets": 1204,
      "empreinte": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
      "maj_le": "2026-07-28T14:03:02.554Z"
    },
    {
      "chemin": "layout/theme.liquid",
      "octets": 3987,
      "empreinte": "486ea46224d1bb4fb680f34f7c9ad96a8f24ec88be73ea8e5a6c65260e9cb8a7",
      "maj_le": "2026-07-28T14:03:02.554Z"
    }
  ]
}
404 — thème inconnu, ou d’un autre site
json
{ "error": "introuvable" }

Portée lecture. Les chemins sont triés par ordre alphabétique, et le contenu n’est pas rendu : un thème complet pèse plusieurs centaines de kilo-octets, et l’éditeur n’affiche qu’un fichier à la fois.

themeobjet

id, nom et role du thème. role vaut publie ou brouillon — c’est ce que le CLI regarde avant de refuser de suivre un thème en ligne.

fichiers[].cheminchaîne

Toujours deux segments : dossier/fichier. Voir Arborescence pour les six dossiers et leurs extensions.

fichiers[].octetsentier

La taille réelle en octets UTF-8 du contenu. Un « é » y pèse 2.

fichiers[].empreintechaîne

Le SHA-256 hexadécimal du contenu, encodé en UTF-8. Calculé à la volée, jamais stocké.

fichiers[].maj_lehorodatage

Dernière écriture de ce fichier précis.

#À quoi sert l’empreinte

C’est elle qui rend theme pousser supportable. Le CLI calcule le SHA-256 de chacun de ses fichiers locaux, le compare à celui d’ici, et n’envoie que les différents. Sans elle, il faudrait télécharger le thème entier avant chaque envoi juste pour savoir quoi envoyer — quelques centaines de kilo-octets à chaque sauvegarde, pour au plus un fichier modifié.

Attention

octets ne peut pas jouer ce rôle. Corriger une faute de frappe, changer une couleur hexadécimale ou permuter deux lignes laisse le fichier exactement aussi long : un client qui se fierait à la taille sauterait précisément les modifications qu’on fait le plus souvent.

Elle n’est stockée nulle part, et c’est délibéré. Le contenu est de toute façon lu par cette requête — il faut bien le lire pour donner octets — tandis qu’une colonne d’empreinte en base serait une seconde source de vérité à tenir à jour à chaque écriture, donc une occasion de mentir le jour où on l’oublierait quelque part.

Ne pousser que ce qui a changébash
# L'arborescence distante, ramenée à « chemin empreinte »
curl -s "https://cms.webcosa.com/api/themes/$THEME/fichiers" \
  -H "Authorization: Bearer $WEBCOSA_CLE" \
  | jq -r '.fichiers[] | "\(.chemin) \(.empreinte)"' > distant.txt

# La même chose en local
find layout sections snippets assets config templates -type f \
  -exec shasum -a 256 {} \; | awk '{print $2, $1}' | sort > local.txt

#Lire un fichier

GET/api/themes/<id>/fichiers?chemin=sections/hero.liquid
200
json
{
  "fichier": {
    "chemin": "sections/hero.liquid",
    "contenu": "<section class=\"hero\">\n  <h1>{{ section.settings.titre }}</h1>\n</section>\n",
    "maj_le": "2026-08-05T09:12:44.108Z"
  }
}
404 — ce chemin n’existe pas dans ce thème
json
{ "error": "fichier_introuvable" }

Le paramètre chemin est comparé à l’identique, sans normalisation : pas de barre oblique de tête, pas de ./, la casse compte. Pensez à l’encoder — la barre oblique passe telle quelle dans une chaîne de requête, mais un espace ou un accent, non.

Notez les deux codes distincts : introuvable désigne le thème, fichier_introuvable le chemin. Un client qui les confondrait chercherait une erreur d’identifiant là où il n’y a qu’une faute de frappe dans un nom de section.

#Écrire un fichier

PUT/api/themes/<id>/fichiers

Corps de la requête

json
{
  "chemin": "sections/temoignages.liquid",
  "contenu": "<section class=\"temoignages\">…</section>\n{% schema %}\n{ \"name\": \"Témoignages\" }\n{% endschema %}\n"
}
200
json
{ "ok": true }
400 — erreur de syntaxe Liquid
json
{ "error": "syntaxe", "message": "tag \"for\" not closed, line:14, col:1" }
400 — chemin refusé
json
{ "error": "hors_dossier", "message": "Ce fichier n'est dans aucun dossier connu. Attendus : layout, sections, snippets, assets, config, templates." }
400 — plafond de fichiers
json
{ "error": "trop_de_fichiers", "message": "Un thème est limité à 120 fichiers." }
402 — site inactif
json
{ "error": "site_inactif", "etat": "expire", "message": "Ce site n'est plus actif : ses modifications sont bloquées. Reprends un abonnement depuis le CMS (Réglages → Abonnement)." }

Écriture idempotente : le chemin existe, il est remplacé ; il n’existe pas, il est créé. Il n’y a pas de POST distinct, et il n’y a pas de version précédente à laquelle revenir.

cheminchaînerequis

De 3 à 120 caractères, exactement deux segments. Voir les refus ci-dessous.

contenuchaînerequis

Le fichier entier, en clair. Plafonné à 524 288 — attention, des unités de code UTF-16, pas des octets : un fichier plein d’accents passe la barre un peu plus tard qu’un ls -l ne le laisse croire. C’est la même mesure partout dans le produit, et elle est volontairement large.

Une chaîne vide est acceptée : un fichier peut être vidé sans être supprimé.

#Les quatre refus de chemin

errorCe qui l’a déclenché
traverseeLe chemin commence par /, contient .. ou une barre oblique inversée
hors_dossierPas exactement deux segments, ou premier segment inconnu
nomLe nom du fichier ne commence pas par une lettre ou un chiffre, ou contient autre chose que lettres, chiffres, points, tirets et tirets bas
extensionExtension non admise dans ce dossier — un .js dans assets/, un .liquid dans config/
Danger

traversee est la faille classique des archives, celle qui permet d’écrire ailleurs que là où on croit. Elle est vérifiée à l’entrée, pas au moment d’écrire : un seul endroit à relire.

#La syntaxe est vérifiée avant d’écrire

Un fichier Liquid mal formé casse la page où il est rendu. Il est donc analysé avant d’être enregistré, et le refus porte le message de l’analyseur.

C’est ce qui distingue un éditeur de code utilisable d’un champ de texte : on apprend l’erreur en enregistrant, pas en visitant son site.

ExtensionCe qui est vérifié
.jsonJSON.parse. Le message reprend celui de l’analyseur JSON
.liquidL’analyse Liquid, le bloc {% schema %} retiré au préalable
.css, .svgRien. Un CSS invalide dégrade l’apparence, il ne casse pas le rendu
Attention

Le contrôle porte sur la syntaxe, pas sur le sens. {{ nawak }} passe — comme chez Shopify — et rend du vide. Aucun {% render %} n’est résolu à ce stade : appeler un snippet qui n’existe pas ne se voit qu’au rendu de la page.

Le {% schema %} d’une section n’est pas validé ici. Un schéma au JSON malformé s’écrit sans erreur ; la section reste rendable, mais sans réglages modifiables dans l’éditeur.

#Le plafond de fichiers

120 par thème, et il n’est contrôlé qu’à la création. Un thème déjà au plafond reste modifiable — sinon on ne pourrait plus corriger celui qui dépasse. Autrement dit, PUT sur un chemin existant ne compte jamais.

curl -s -X PUT "https://cms.webcosa.com/api/themes/$THEME/fichiers" \-H "Authorization: Bearer $WEBCOSA_CLE" \-H "Content-Type: application/json" \-d "$(jq -n --arg c "$(cat sections/hero.liquid)" \ '{chemin:"sections/hero.liquid", contenu:$c}')"{"ok":true}

Une écriture réussie met aussi à jour le maj_le du thème, pas seulement celui du fichier : c’est lui qui ordonne la liste des thèmes.

#Supprimer un fichier

DELETE/api/themes/<id>/fichiers?chemin=snippets/vieux.liquid
200
json
{ "ok": true }
400 — paramètre chemin absent
json
{ "error": "invalid_input" }
409 — fichier indispensable
json
{
  "error": "fichier_requis",
  "message": "Ce fichier est indispensable au thème : il ne peut pas être supprimé."
}

Trois chemins sont protégés, et la liste est close :

Ce qui ne se supprime pas
layout/theme.liquid
templates/index.json
config/settings_schema.json

Sans coquille ni template, le thème ne rend plus rien et le site tombe. Ces trois fichiers se remplacent par un PUT, ils ne se retirent pas.

Note

Supprimer un chemin qui n’existe pas répond 200 ok. La suppression est idempotente : rejouer une requête après un délai réseau ne doit pas produire une erreur qui enverrait chercher un problème inexistant.

Le CLI, lui, ne supprime jamais par défaut : theme pousser signale les fichiers présents à distance et absents en local, sans les toucher. Il faut --supprimer, et une confirmation. Ces fichiers n’existent qu’en base — il n’y a ni corbeille ni version précédente.

#L’ordre des contrôles

RangContrôleRefus
1Clé valable, portée suffisante, site accessible401, 403, 409
2Le thème est bien du site404 introuvable
3Site actif — écritures seulement402 site_inactif
4Corps ou paramètre conforme400 invalid_input
5Chemin admis par le format400 traversee, hors_dossier, nom, extension
6Syntaxe du fichier400 syntaxe
7Plafond de fichiers, à la création400 trop_de_fichiers

Le 404 du thème passe avant la garde de facturation : un identifiant erroné sur un site suspendu répond 404, pas 402.