كيفية إنشاء نسخة احتياطية لأنماط Squarespace المُدارة باستخدام سحابة AWS
مقدمة: لماذا تحتاج إلى نسخة احتياطية لأنماط Squarespace؟
في بعض المشاريع، قد يبدو تنفيذ تعديل بصري متقدم داخل منصة Squarespace أمراً بسيطاً: تكتب أنماط CSS أو JavaScript مخصصة، ثم تضيفها إلى الموقع بالاعتماد على وثائق المنصة. لكن المشكلة الحقيقية لا تظهر دائماً أثناء التطوير، بل عند اعتماد الموقع على ملف أنماط خارجي مستضاف على خوادم لا تملك أنت السيطرة عليها.
إذا تعطل تحميل ملف الأنماط الرئيسي، فقد يبدو الموقع كاملاً وكأنه مكسور، رغم أن القوالب أو المحتوى لم تتغير. هنا تظهر أهمية وجود نسخة احتياطية ذكية لملفات التصميم، بحيث يستمر الموقع في العمل حتى عند تعطل المصدر الأساسي.

بداية المشكلة: أين ذهبت أنماط الموقع؟
أثناء تعديل بسيط داخل محرر الأنماط في Squarespace، مثل تغيير قيمة margin باستخدام متغير، اختفت المعاينة المباشرة فجأة. وبعد فتح الموقع في تبويب جديد، تبيّن أن جميع الصفحات فقدت تنسيقها بالكامل.

في البداية، بدا الأمر كأنه خطأ في الشيفرة المخصصة. تم حذف المتغير، ثم استبداله بقيم عادية، ثم إزالة كل أنماط CSS المخصصة، لكن المشكلة استمرت. عدم ظهور أخطاء في وحدة التحكم Console زاد من غموض الموقف.
عندها ظهر السؤال الأهم: لماذا لا يتم تحميل الأنماط أصلاً؟
بفحص شجرة الصفحة HTML، اتضح أن الموقع يعتمد على ملف أنماط خارجي باسم site.css يتم تحميله من خوادم Squarespace.

ثم من خلال تبويب Network في أدوات المطور، ظهر أن الطلب الموجه إلى هذا الملف يرجع خطأ من فئة 5xx، ما يعني أن المشكلة من جهة الخادم وليس من الشيفرة التي تمت إضافتها.

وبمراجعة صفحة الحالة الخاصة بـ Squarespace، تبيّن بالفعل وجود عطل في بعض خدماتهم تسبب في فشل تحميل الأنماط لعدد من المستخدمين.
لماذا كان لا بد من حل دائم؟
قد يبدو العطل الأول مجرد حالة نادرة، لكن تكرار المشكلة لاحقاً خلال ساعات العمل ولفترة أطول كشف أن الاعتماد الكامل على ملف أنماط مستضاف خارج نطاق التحكم يمثل مخاطرة تشغيلية حقيقية.
المشكلة الأساسية كانت واضحة:
- ملفات الأنماط الأساسية مستضافة على بنية لا يمكن التحكم بها.
- أي تعطل في خوادم الطرف الثالث ينعكس مباشرة على مظهر الموقع.
- العميل أو فريق التصميم قد يظن أن التعديلات البرمجية هي سبب الانهيار.
الحل المنطقي كان إنشاء مسار احتياطي Fallback يسمح للموقع بتحميل نسخة بديلة من الأنماط عند فشل الملف الأصلي.
الحل الأول باستخدام AWS: نسخة احتياطية مبنية على CodeCommit وLambda وS3
في البداية، كانت الأنماط المخصصة تُكتب بصيغة SASS داخل محرر Squarespace، مع الاحتفاظ بنسخة محلية داخل مستودع Git. ومن هنا ظهرت فكرة استضافة نسخة جاهزة من ملف CSS على AWS S3، مع تحديثها تلقائياً عند كل تعديل.

آلية العمل في الحل الأول
- رفع ملف
SASSإلى مستودعCodeCommit. - تشغيل دالة
Lambdaتلقائياً عند كل عمليةpush. - قراءة ملف
SASSوتحويله إلىCSS. - رفع الملف الناتج إلى حاوية
S3عامة. - إضافة سكربت داخل الموقع ليتحقق من تحميل
site.css، وإذا فشل، يتم تحميل ملف احتياطي منS3.
إعداد المشغّل Trigger في CodeCommit
بعد رفع الشيفرة إلى المستودع، يتم إنشاء مشغّل من إعدادات المستودع عبر تبويب Triggers، مع تحديد:
- نوع الحدث:
Push to existing branch - الفرع المستهدف:
master - الخدمة الهدف:
AWS Lambda - الدالة المطلوب تشغيلها

منطق دالة Lambda
المهمة هنا بسيطة: جلب ملف SASS من CodeCommit، معالجته عبر مكتبة node-sass، ثم رفع ملف CSS النهائي إلى S3.
const { S3, CodeCommit, } = require('aws-sdk')
const sass = require('node-sass');
const getFileFromCodeCommit = (filePath) => new Promise((resolve, reject) => {
const ccClient = new CodeCommit({ region: "us-east-1" })
const ccParams = {
filePath,
repositoryName: 'mebbels-assets'
}
ccClient.getFile(ccParams, (err, data) => {
if (err) reject(err)
console.log(data)
let stringData = new TextDecoder().decode(data.fileContent);
resolve(stringData)
})
})
const sendStylesheetToS3 = (fileData, fileName) => new Promise((resolve, reject) => {
const s3Client = new S3({ region: "eu-south-1" })
let putObjectBody = {
Bucket: 'mebbels-assets',
Key: fileName,
ACL: 'public-read',
Body: fileData,
ContentType: 'text/css'
}
s3Client.putObject(putObjectBody, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
const processSASS = (fileData) => new Promise((resolve, reject) => {
sass.render({ data: fileData }, (err, data) => {
if (err) reject(err)
resolve(data)
})
})
exports.handler = async (event) => {
const sassFile = await getFileFromCodeCommit('mebbels-sass.scss')
const processedSass = await processSASS(sassFile)
await sendStylesheetToS3(processedSass.css, 'fallbackStyles.css')
const response = {
statusCode: 200,
body: JSON.stringify("Done"),
};
return response;
};
صلاحيات IAM المطلوبة
حتى تعمل الدالة دون مشكلات، يجب منحها صلاحيات مناسبة للوصول إلى CodeCommit وS3.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"codecommit:GitPull",
"s3:PutObjectAcl",
"codecommit:GetFile"
],
"Resource": [
"arn:aws:s3:::*/*",
"arn:aws:s3:::mebbels-assets",
"arn:aws:codecommit:us-east-1:6653912857032:mebbels-assets"
]
}
]
}
إعداد حاوية S3
لكي يتمكن الموقع من طلب ملف الأنماط الاحتياطي، يجب أن تكون الحاوية عامة، مع ضبط CORS للسماح بالطلبات القادمة من نطاق الموقع.

[
{
"AllowedHeaders": ["Authorization"],
"AllowedMethods": ["GET"],
"AllowedOrigins": ["https://www.mebbels.com"],
"ExposeHeaders": [],
"MaxAgeSeconds": 3000
}
]
السكربت المسؤول عن تفعيل النمط الاحتياطي
هذا السكربت يوضع داخل رأس الصفحة، ويتحقق من تحميل ملف site.css. وإذا لم يتم تحميله خلال فترة قصيرة، يضيف رابطاً إلى ملف CSS الاحتياطي المستضاف على S3.
var isSiteCssLoaded = false;
var siteCssLink = document.querySelector("link[href*='/site.css']")
siteCssLink.addEventListener('load', () => {
console.log('site.css loaded')
isSiteCssLoaded = true;
})
const fallBackIfNeeded = () => {
if (!isSiteCssLoaded) {
console.log('site.css not loaded')
var headID = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.type = 'text/css';
link.rel = 'stylesheet';
link.href = 'https://mebbels-assets.s3.eu-south-1.amazonaws.com/fallbackStyles.css'
headID.appendChild(link);
console.log('fallback styles loaded')
}
}
setTimeout(fallBackIfNeeded, 20)
كيف تختبر الحل عملياً؟
لا حاجة إلى انتظار تعطل خوادم Squarespace مجدداً. يمكن محاكاة المشكلة مباشرة من أدوات المطور داخل المتصفح:
- فتح تبويب
Network. - تعطيل الذاكرة المؤقتة
Disable cache. - حظر عنوان طلب ملف
site.css. - إعادة تحميل الصفحة.

إذا كان كل شيء مضبوطاً بشكل صحيح، سيظهر في Console أن الملف الأساسي لم يُحمّل، ثم يتم تحميل ملف الأنماط الاحتياطي ويستمر الموقع في العمل دون كسر التصميم.

لماذا لم يكن الحل الأول مثالياً؟
رغم أن الحل الأول عملي وسريع ومنخفض التكلفة، فإنه يعتمد على تدخل المطور بشكل مستمر. أي تعديل في الأنماط داخل Squarespace يجب نسخه يدوياً إلى المستودع، وإلا ستصبح النسخة الاحتياطية قديمة.
وهذا يخلق عدة تحديات:
- الاعتماد على تحديث يدوي متواصل.
- إمكانية تعديل الأنماط من قبل مصممين أو مديري موقع آخرين دون علم المطور.
- خطر عدم تطابق النسخة الاحتياطية مع النسخة الفعلية المستخدمة في الموقع.
من هنا ظهرت الحاجة إلى حل أكثر ذكاءً وأقل اعتماداً على العنصر البشري.
الحل الأفضل: أتمتة النسخ الاحتياطي باستخدام CloudWatch Events
الفكرة المطورة تقوم على تشغيل دالة Lambda بشكل دوري عبر CloudWatch Events، بحيث تزحف الدالة إلى الموقع، تجمع جميع ملفات الأنماط الخارجية المرتبطة به، وتدمجها في ملف واحد يتم رفعه إلى S3.
بهذه الطريقة، لا يعود المطور مسؤولاً عن تحديث النسخة الاحتياطية يدوياً، بل يتم تحديثها تلقائياً كل عدة ساعات.

آلية العمل بعد التحسين
- إنشاء حدث مجدول في
CloudWatch. - تشغيل دالة
Lambdaكل فترة زمنية محددة. - زيارة الصفحة الرئيسية للموقع وتحليل عناصر
linkالخاصة بملفاتCSS. - تحميل كل ملفات الأنماط الخارجية ودمجها في ملف واحد.
- رفع الملف الناتج إلى
S3ليُستخدم كنسخة احتياطية.
كود Lambda الجديد بلغة Python
import sys, os
import urllib.request as req
from bs4 import BeautifulSoup
import logging
import boto3
from botocore.exceptions import ClientError
s3_client = boto3.client('s3')
def lambda_handler(event, context):
fallback_css_filename = 'fallbackStyles.css'
fallback_css_path = '/tmp/' + fallback_css_filename
url = 'https://www.mebbels.com'
html = req.urlopen(url) # request the initial page
soup = BeautifulSoup(html, 'html.parser')
fallback_styles = open(fallback_css_path, 'ab')
for link in soup.find_all('link', type='text/css'): # get links to external style sheets
address = link['href'] # the address of the stylesheet
if address.startswith('/'):
address = url + address
css_file_name, headers = req.urlretrieve(address)
css = open(css_file_name, 'rb')
fallback_styles.write(css.read())
css.close()
try:
s3_client.upload_file(
fallback_css_path,
'mebbels-assets',
fallback_css_filename,
ExtraArgs={'ACL': 'public-read', 'ContentType': 'text/css'}
)
return True
except ClientError as e:
logging.error(e)
return False
الميزة المهمة هنا أن الدالة لا تكتفي بأنماط Squarespace فقط، بل تستطيع أيضاً حفظ أي ملف أنماط خارجي آخر، مثل Google Fonts أو مكتبات مثل Bootstrap.
إعداد CloudWatch Events للتشغيل المجدول
يمكن إنشاء قاعدة جديدة من خلال المسار CloudWatch > Events > Rules، ثم تحديد:
- مصدر الحدث:
Schedule - الفاصل الزمني: مثلاً كل 6 ساعات
- الهدف: دالة
Lambdaالمحددة

بعد ذلك يتم إدخال اسم القاعدة ووصفها فقط، بينما تتكفل AWS تلقائياً بمنح الإذن اللازم للحدث كي يشغّل دالة Lambda.

مقارنة سريعة بين الحلين
| العنصر | الحل الأول | الحل الثاني |
|---|---|---|
| مصدر التحديث | يدوي عبر CodeCommit |
تلقائي عبر CloudWatch |
| الاعتماد على المطور | مرتفع | منخفض |
| مخاطر نسيان التحديث | مرتفعة | محدودة |
| تغطية الملفات الخارجية | عادة ملف محدد | جميع ملفات CSS الخارجية |
| القابلية للتوسع | متوسطة | أفضل |
أفضل الممارسات عند تنفيذ هذا النوع من الحلول
- استخدم اسماً واضحاً لملف الأنماط الاحتياطي مثل
fallbackStyles.css. - راقب صلاحيات
IAMبدقة، وامنح أقل قدر ممكن من الصلاحيات. - اختبر سيناريو الفشل بشكل دوري، وليس مرة واحدة فقط.
- تأكد من تحديث إعدادات
CORSبما يتوافق مع نطاقات موقعك. - إذا كان الموقع يعتمد على ملفات كثيرة، ففكّر في ضغط الملف النهائي أو إصدار نسخة
minified.
متى يكون هذا الحل مناسباً؟
هذا النهج مناسب جداً إذا كنت:
- تدير موقعاً على
Squarespaceأو أي منصة إنشاء مواقع مشابهة. - تعتمد على ملفات أنماط خارجية حيوية للمظهر العام.
- تحتاج إلى تقليل أثر الانقطاعات المفاجئة على تجربة المستخدم.
- ترغب في حل منخفض التكلفة ويعتمد على خدمات سحابية بدون خوادم دائمة
Serverless.
الخلاصة التقنية
الاعتماد الكامل على ملفات أنماط مستضافة لدى طرف ثالث قد يكون نقطة ضعف خفية في استقرار الموقع. استخدام AWS لبناء مسار احتياطي لأنماط Squarespace هو حل ذكي وعملي، خصوصاً عند أتمتته بواسطة Lambda وS3 وCloudWatch. تقنياً، الحل الثاني هو الأفضل لأنه يقلل التدخل اليدوي ويرفع موثوقية النسخة الاحتياطية، ما يجعله أكثر ملاءمة للمواقع التجارية التي لا تحتمل تعطل الواجهة ولو لدقائق.