#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
/api/themes/<id>/fichiers{
"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"
}
]
}{ "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.
themeobjetid, 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îneToujours deux segments : dossier/fichier. Voir Arborescence
pour les six dossiers et leurs extensions.
fichiers[].octetsentierLa taille réelle en octets UTF-8 du contenu. Un « é » y pèse 2.
fichiers[].empreintechaîneLe SHA-256 hexadécimal du contenu, encodé en UTF-8. Calculé à la volée, jamais stocké.
fichiers[].maj_lehorodatageDerniè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é.
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.
# 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
/api/themes/<id>/fichiers?chemin=sections/hero.liquid{
"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"
}
}{ "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
/api/themes/<id>/fichiersCorps de la requête
{
"chemin": "sections/temoignages.liquid",
"contenu": "<section class=\"temoignages\">…</section>\n{% schema %}\n{ \"name\": \"Témoignages\" }\n{% endschema %}\n"
}{ "ok": true }{ "error": "syntaxe", "message": "tag \"for\" not closed, line:14, col:1" }{ "error": "hors_dossier", "message": "Ce fichier n'est dans aucun dossier connu. Attendus : layout, sections, snippets, assets, config, templates." }{ "error": "trop_de_fichiers", "message": "Un thème est limité à 120 fichiers." }{ "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înerequisDe 3 à 120 caractères, exactement deux segments. Voir les refus ci-dessous.
contenuchaînerequisLe 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
error | Ce qui l’a déclenché |
|---|---|
traversee | Le chemin commence par /, contient .. ou une barre oblique inversée |
hors_dossier | Pas exactement deux segments, ou premier segment inconnu |
nom | Le nom du fichier ne commence pas par une lettre ou un chiffre, ou contient autre chose que lettres, chiffres, points, tirets et tirets bas |
extension | Extension non admise dans ce dossier — un .js dans assets/, un .liquid dans config/ |
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.
| Extension | Ce qui est vérifié |
|---|---|
.json | JSON.parse. Le message reprend celui de l’analyseur JSON |
.liquid | L’analyse Liquid, le bloc {% schema %} retiré au préalable |
.css, .svg | Rien. Un CSS invalide dégrade l’apparence, il ne casse pas le rendu |
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
/api/themes/<id>/fichiers?chemin=snippets/vieux.liquid{ "ok": true }{ "error": "invalid_input" }{
"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 :
layout/theme.liquid
templates/index.json
config/settings_schema.jsonSans 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.
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
| Rang | Contrôle | Refus |
|---|---|---|
| 1 | Clé valable, portée suffisante, site accessible | 401, 403, 409 |
| 2 | Le thème est bien du site | 404 introuvable |
| 3 | Site actif — écritures seulement | 402 site_inactif |
| 4 | Corps ou paramètre conforme | 400 invalid_input |
| 5 | Chemin admis par le format | 400 traversee, hors_dossier, nom, extension |
| 6 | Syntaxe du fichier | 400 syntaxe |
| 7 | Plafond de fichiers, à la création | 400 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.

