Ադմին վահանակներ և CMS
Ինչ է պետք իրական back office-ին՝ դերեր, CRUD, մեդիա, SEO, հաշվետվություններ, export-ներ և audit log-եր։
30 վայրկյանում
Ադմին վահանակն այն պրոդուկտն է, որը բիզնեսն օգտագործում է ամեն օր՝ ապրանքների, պատվերների, հաճախորդների, կոնտենտի և ակցիաների կառավարման համար։ Նրան պետք են մանրամասն դերեր, արագ ցուցակներ ֆիլտրերով, մեդիա գրադարան, SEO դաշտեր, հաշվետվություններ և export-ներ, ինչպես նաև audit log, որը պատասխանում է «ո՞վ է սա փոխել» հարցին։ Այն կոնֆիգուրացիայից կառուցելը՝ յուրաքանչյուր էկրանը ձեռքով գրելու փոխարեն, պահում է այն համաձայնեցված և էժան ընդլայնվող։
Ինչու եմ օգտագործում
- Ես օգտագործում եմ config-driven ադմին վահանակներ, որովհետև սպասարկում եմ ներքին React ադմին համակարգ՝ կարգավորելի CRUD ռեսուրսներով, մեդիա մենեջերով և design system-ով, որը կրկին օգտագործվում է հաճախորդների բազմաթիվ նախագծերում։
- Հաճախորդները տեսնում են storefront-ը, բայց աշխատակիցներն ապրում են ադմինում։ Դանդաղ կամ շփոթեցնող ադմինը բիզնեսին ամեն օր ժամեր է արժենում։
- Ադմինի էկրանների մեծ մասը նույն pattern-ն է՝ ցուցակ, ֆիլտր, ստեղծել, խմբագրել, ջնջել, ուստի դրանք config-ից գեներացնելը ժամանակ է ազատում իսկապես հատուկ էկրանների համար։
- Դերերը, audit log-երը և export-ներն են համակարգը վստահելի դարձնում աճող թիմի համար։
Ինչպես է աշխատում
01Ինչ է պետք յուրաքանչյուր ադմինի
Սկսեք ադմինի օգտատերերից, ոչ թե տվյալների բազայի աղյուսակներից։ Պատվերներով զբաղվող օպերատորին պետք է պատվերների արագ ցուցակ՝ կարգավիճակի ֆիլտրերով և իրական ժամանակի թարմացումներով։ Կոնտենտ մենեջերին պետք են էջեր, բլոգ, մեդիա և SEO։ Մարքեթոլոգին պետք են ակցիաներ, սեգմենտներ և արշավներ։ Մենեջերին պետք են հաշվետվություններ։ Նրանց բոլորի հետևում նույն շինանյութն է՝ նույնականացում (իդեալում՝ երկրորդ գործոնով կամ մեկանգամյա կոդերով ադմինների համար), դերեր և թույլտվություններ, CRUD էկրաններ (create, read, update, delete)՝ որոնմամբ, ֆիլտրերով, դասավորմամբ և pagination-ով, զանգվածային գործողություններ, մեդիա գրադարան, կարգավորումներ, հաշվետվություններ export-ներով և audit log։ Ծանուցումները՝ web push կամ in-app, աշխատակիցներին տեղեկացնում են նոր պատվերների կամ ձախողված sync-երի մասին։
- Ցուցակներ՝ server-side pagination, ֆիլտրերը URL-ում, որպեսզի դիտումները կարելի լինի կիսել։
- Ձևեր՝ վալիդացիա սերվերում, հստակ սխալներ, նախազգուշացում չպահպանված փոփոխությունների մասին։
- Զանգվածային գործողություններ՝ հաստատմամբ և փոփոխվածի ամփոփումով։
02Դերեր, թույլտվություններ և audit log-եր
Դերը թույլտվությունների անվանված փաթեթ է («պատվերների օպերատոր», «կոնտենտի խմբագիր»), իսկ թույլտվությունը մեկ գործողություն է ռեսուրսի վրա, օրինակ՝ orders.view կամ products.update։ Թույլտվությունները ստուգեք սերվերում յուրաքանչյուր հարցման համար․ UI-ում կոճակը թաքցնելը հարմարություն է, ոչ թե անվտանգություն։ Թույլտվությունների անունները պահեք հետևողական, որպեսզի ադմինի մենյուն, route-երը և կոճակները կարողանան կարդալ նույն ցուցակը։ Զգայուն գործողությունների համար, օրինակ՝ վերադարձներ, մնացորդի ձեռքով փոփոխություններ կամ դերերի փոփոխություններ, ավելացրեք լրացուցիչ դիմադրություն՝ պատճառի դաշտ, հաստատում, գուցե երկրորդ հաստատող։ Audit log-ը գրանցում է, թե ով ինչ արեց, երբ, որ գրառման հետ, և արժեքները՝ առաջ և հետո։ Երբ production-ում ինչ-որ բան սխալ է գնում, audit log-ը գուշակությունը վերածում է երկու րոպեանոց որոնման։
// Laravel: one permission check per action, enforced on the server
Route::middleware(['auth:admin', 'can:orders.refund'])
->post('/admin/orders/{order}/refund', RefundOrderController::class);
// Audit every sensitive change with before/after values
AuditLog::create([
'admin_id' => $request->user()->id,
'action' => 'order.refunded',
'subject' => $order->getMorphClass().':'.$order->id,
'before' => ['status' => $before],
'after' => ['status' => $order->status, 'amount' => $amount],
'reason' => $request->string('reason'),
'ip' => $request->ip(),
]);
03Config-driven CRUD
Ադմինի էկրանների մեծ մասը տարբերվում է միայն դաշտերով և սյունակներով։ Յուրաքանչյուր ցուցակ և ձև ձեռքով գրելու փոխարեն ռեսուրսը նկարագրեք կոնֆիգուրացիայում՝ API endpoint-ը, ցուցակի սյունակները, ֆիլտրերը, ձևի դաշտերը տիպերով (text, number, select, rich text, image, slug, կատեգորիաների ծառ) և թույլտվությունները։ Ընդհանուր շարժիչը այդ նկարագրությունից ցուցադրում է ցուցակն ու ձևը, այնպես որ նոր ռեսուրսը րոպեներ է պահանջում, և յուրաքանչյուր էկրան ստանում է նույն pagination-ը, վալիդացիայի ցուցադրումը և զանգվածային գործողությունները։ Backend-ում ընդհանուր resource controller-ը կարող է սպասարկել ստանդարտ CRUD-ը, իսկ custom endpoint-ները՝ հատուկ տրամաբանությունը։ Պահեք «escape hatch»․ որոշ էկրաններ՝ պատվերի մանրամասների էջ, ակցիաների կառուցիչ, dashboard, արժանի են custom կոմպոնենտների, և config համակարգը պետք է թույլ տա դրանք միացնել։
export const brandsResource = {
name: "brands",
endpoint: "/admin/brands",
permission: "brands",
list: {
columns: ["logo", "name", "productsCount", "isActive"],
filters: [{ field: "isActive", type: "boolean" }],
search: ["name"],
},
form: [
{ name: "name", type: "text", required: true, translatable: true },
{ name: "slug", type: "slug", from: "name" },
{ name: "logo", type: "media", accept: "image/*" },
{ name: "description", type: "richText", translatable: true },
{ name: "seo", type: "seo" },
],
} as const;
04Մեդիա գրադարան և SEO դաշտեր
Մեդիա գրադարանը վերբեռնումները պահում է մեկ անգամ և թույլ է տալիս խմբագիրներին կրկին օգտագործել դրանք՝ թղթապանակներ, որոնում, crop, փոխարինում (հղումները պահպանելով), alt տեքստ և օգտագործման հետևում։ Վալիդացրեք վերբեռնումները ըստ բովանդակության, ոչ միայն ֆայլի ընդլայնման, սահմանափակեք չափն ու չափսերը, հեռացրեք metadata-ն և գեներացրեք փոքրացված տարբերակներ, օրինակ՝ WebP thumbnail-ներ, background job-ում։ Երբ կայքն աճում է, ֆայլերը պահեք object storage-ում (S3-համատեղելի) CDN-ի հետևում, ոչ թե հավելվածի սերվերի դիսկում։ SEO դաշտերը պետք է լինեն յուրաքանչյուր հանրային մոդելի վրա՝ slug, meta title, meta description, social image, canonical URL և noindex անջատիչ, կոնտենտից գեներացված խելամիտ default-ներով։ Ադմինը պետք է նաև կառավարի redirect-ները, որովհետև վերանվանված slug-երը այլապես կոտրված հղումներ են ստեղծում, իսկ կայքը պետք է իր sitemap-ը գեներացնի նույն տվյալներից։
05Հաշվետվություններ և export-ներ
Հաշվետվությունները պատասխանում են բիզնես հարցերի՝ վաճառքներն ըստ օրերի և ալիքների, լավագույն ապրանքներ, նոր և վերադարձող հաճախորդներ, ակցիաների արժեք, առաքման միջին ժամանակ։ Կառուցեք դրանք հաշվետվությունների համար նախատեսված query-ների վրա՝ ագրեգացված աղյուսակներ, read replica-ներ կամ գիշերային ամփոփումներ, որպեսզի ծանր հաշվետվությունը երբեք չդանդաղեցնի checkout-ը։ Միշտ ցույց տվեք ամսաթվերի միջակայքը և ժամային գոտին, և համոզվեք, որ թվերը համընկնում են պատվերների ցուցակի հետ («վճարվածի» նույն սահմանումն ամենուր)։ Excel կամ CSV export-ները էական են, քանի որ ֆինանսներն ու մարքեթինգն ապրում են աղյուսակներում։ Մեծ export-ները գեներացրեք queue-ի job-ում, տողերը stream արեք՝ ամեն ինչ հիշողության մեջ բեռնելու փոխարեն, և պատրաստ լինելուն պես ուղարկեք ներբեռնման հղում։ Export-ները պարունակում են անձնական տվյալներ, ուստի պաշտպանեք դրանք թույլտվություններով, կարճաժամկետ հղումներով և audit գրառումով։
06Ներկառուցված CMS, թե՞ headless CMS
CMS-ը (content management system) թույլ է տալիս ոչ ծրագրավորողներին խմբագրել էջեր և կոնտենտ։ Ներկառուցված CMS-ն ապրում է ձեր սեփական ադմինի և տվյալների բազայի ներսում՝ էջեր, բլոգ, FAQ, բաններներ և բլոկներ՝ ապրանքների և պատվերների կողքին։ Այն իդեալական է, երբ կոնտենտը սերտորեն կապված է commerce տվյալների հետ, և ոչ մի լրացուցիչ արժեք չունի։ Headless CMS-ը (Contentful, Sanity, Strapi, Payload) առանձին ծառայություն է, որը պահում է կոնտենտը և մատուցում այն API-ով․ այն փայլում է խմբագրական թիմերի, հարուստ բլոկային layout-ների, preview-ների և workflow-ների համար։ Հիբրիդը տարածված է՝ կառուցվածքային բիզնես տվյալները ձեր սեփական backend-ում, խմբագրական կոնտենտը՝ headless CMS-ում։ Ինչ էլ ընտրեք, կոնտենտը մոդելավորեք որպես տիպավորված բլոկներ, ոչ թե մեկ հսկա HTML դաշտ, որպեսզի frontend-ը վերահսկի դիզայնը, իսկ կոնտենտը մնա շարժական։
Օգտագործեք, երբ
- Ցանկացած պրոդուկտ, որտեղ աշխատակիցները ամեն օր կառավարում են տվյալներ՝ խանութներ, պատվերների պլատֆորմներ, CRM-ներ, ամրագրման համակարգեր։
- Մի քանի նախագծի պետք են նմանատիպ ադմին էկրաններ, ինչը reusable config-driven համակարգը արժանի է դարձնում կառուցելու։
- Տարբեր պատասխանատվություններ ունեցող թիմերին պետք են հասանելիության տարբեր մակարդակներ և հաշվետվողականություն։
Մի՛ օգտագործեք, երբ
- Ստատիկ մարքեթինգային կայք՝ հազվադեպ խմբագրումներով․ headless CMS-ը կամ նույնիսկ Markdown ֆայլերն ավելի պարզ են։
- Երբ պատրաստի ադմին framework-ը լավ է համապատասխանում ձեր stack-ին․ ստանդարտ դեպքի համար մի վերակառուցեք Filament-ը կամ Django admin-ը։
- Խուսափեք ընդհանուր CRUD-ից բարդ workflow-ների համար, օրինակ՝ պատվերների մշակում․ դրանց պետք են հատուկ նախատեսված էկրաններ։
Այլընտրանքներ
- Filament / Laravel Nova
Ադմին վահանակի framework-ներ Laravel-ի համար․ շատ արագ են CRUD-ի համար՝ ռեսուրսներով, ձևերով, աղյուսակներով և թույլտվություններով։
- React Admin / Refine
React framework-ներ, որոնք ադմին UI են գեներացնում data provider-ներից․ լավ են custom backend-ների համար։
- Headless CMS (Contentful, Sanity, Strapi, Payload)
Լավագույնն են խմբագրական կոնտենտի, preview-ների և workflow-ների համար․ ավելացնում են ևս մեկ ծառայություն և արժեք։
- Retool / ներքին գործիքների կառուցիչներ
Արագ ներքին dashboard-ներ տվյալների բազաների և API-ների վրա․ ավելի քիչ են հարմար հաճախորդներին ուղղված կոնտենտի համար։
Լավագույն պրակտիկաներ
- Թույլտվությունները պարտադրեք սերվերում յուրաքանչյուր endpoint-ի համար․ UI-ը միայն արտացոլում է դրանք։
- Ադմինի login-ի համար օգտագործեք երկրորդ գործոն կամ մեկանգամյա կոդեր։
- Զգայուն գործողությունները log արեք՝ առաջ/հետո արժեքներով, հեղինակով և պատճառով։
- Ստանդարտ CRUD-ը գեներացրեք կոնֆիգուրացիայից, իսկ custom էկրանները պահեք բարդ workflow-ների համար։
- Վերբեռնումները վալիդացրեք ըստ բովանդակության և նկարները մշակեք background job-երում։
- Յուրաքանչյուր հանրային մոդելի տվեք SEO դաշտեր և redirect-ները կառավարեք ադմինում։
- Ծանր հաշվետվություններն ու export-ներն աշխատեցրեք queue-ներում՝ checkout-ի տվյալների բազայի բեռից հեռու։
Տարածված սխալներ
- Մեկ «admin» դեր բոլորի համար, այնպես որ կոնտենտի խմբագիրը կարող է վերադարձներ անել։
- UI-ում կոճակներ թաքցնել, բայց API-ն անպաշտպան թողնել։
- Ամբողջ աղյուսակները բեռնել բրաուզեր՝ server-side pagination-ի փոխարեն։
- Export-ներ, որոնք կառուցվում են վեբ հարցման մեջ և timeout են տալիս կամ սպառում հիշողությունը։
- Կոնտենտը պահել որպես raw HTML blob-եր, որոնք կոտրվում են դիզայնը փոխելիս։
Խորը վերլուծություն
07Authorisation դերերից այն կողմ. scope-եր և policy-ներ
Role-based access control-ը (RBAC) պատասխանում է «կարո՞ղ է այս մարդը refund անել պատվերները» հարցին, բայց ոչ «կարո՞ղ է նա refund անել այս մասնաճյուղի պատվերները» հարցին։ Իրական back office-ներին պետք են երկուսն էլ։ Permission-ները համատեղեք scope-երի հետ. մասնաճյուղի մենեջերն ունի orders.refund՝ սահմանափակված իր մասնաճյուղերով, մատակարարի օգտատերը տեսնում է միայն իր ապրանքները։ Ստուգումն իրականացրեք յուրաքանչյուր resource-ի համար մեկ տեղում՝ policy class-ում, որն ընդունում է օգտատիրոջը, գործողությունը և գրառումը, և նույն scope-ը կիրառեք ցուցակների query-ների վրա, որպեսզի մարդիկ նույնիսկ չտեսնեն այն տողերը, որոնց չեն կարող դիպչել։ Սա քայլ է դեպի attribute-based access control (ABAC), որտեղ կանոններն օգտագործում են օգտատիրոջ և գրառման ատրիբուտները։ Յուրաքանչյուր օգտատիրոջ permission-ները cache արեք յուրաքանչյուր request-ի համար, invalidate արեք cache-ը, երբ դերերը փոխվում են, և թեստեր գրեք յուրաքանչյուր զգայուն policy-ի համար, որովհետև authorisation-ի bug-երը լուռ են, մինչև տվյալներ չարտահոսեն։
// Laravel policy: permission plus scope in one place
class OrderPolicy
{
public function refund(Admin $admin, Order $order): bool
{
return $admin->can('orders.refund')
&& $admin->branches()->whereKey($order->branch_id)->exists();
}
}
// The same scope applied to list queries
Order::query()
->whereIn('branch_id', $admin->branches()->pluck('branches.id'))
->latest()
->paginate(50);
08Audit log-եր, որոնք դիմանում են միջադեպին
Audit log-ը օգտակար է միայն այն դեպքում, եթե մարդիկ վստահում են դրան։ Գրառումները գրեք service layer-ում, որպեսզի էկրաններից, API կանչերից, import-ներից և console հրամաններից եկող փոփոխությունները բոլորը գրանցվեն։ Ամբողջ տողերի փոխարեն գրանցեք փոխված դաշտերի diff-ը և դիմակավորեք գաղտնիքներն ու զգայուն անձնական տվյալները։ Աղյուսակը դարձրեք append-only. application-ի database օգտատերը կարող է insert անել, բայց ոչ update կամ delete, իսկ retention-ի մաքրումը կատարվում է առանձին արտոնյալ job-ով։ Յուրաքանչյուր գրառման ավելացրեք request ID, որպեսզի admin-ի մեկ գործողությունը, որը դիպչում է տասը գրառման, հետևվի որպես մեկ event։ Բարձր արժեք ունեցող գործողությունների համար log արեք նաև ընթերցումները. ով է export արել հաճախորդների ցուցակը, ով է դիտել քարտապանի ամբողջական հասցեն։ Audit տվյալները պահեք բավականաչափ երկար dispute-ների և կարգավորումների համար և դրանք դարձրեք որոնելի՝ ըստ գրառման, մարդու և ժամանակահատվածի։
-- Append-only for the application user
REVOKE UPDATE, DELETE ON audit_logs FROM app_user;
GRANT INSERT, SELECT ON audit_logs TO app_user;
CREATE INDEX audit_logs_subject_idx ON audit_logs (subject_type, subject_id, created_at DESC);
CREATE INDEX audit_logs_actor_idx ON audit_logs (admin_id, created_at DESC);
09Admin-ի արտադրողականություն մեծ աղյուսակների դեպքում
Admin-ի ցուցակները հաճախ դառնում են համակարգի ամենադանդաղ մասը. միլիոնավոր տողերով պատվերների աղյուսակ, տասնյակ ֆիլտրեր և ընդհանուր count յուրաքանչյուր էջում։ Index-եր ստեղծեք այն ֆիլտրերի համար, որոնք մարդիկ իրականում օգտագործում են, սովորաբար composite index-եր, որոնք սկսվում են status-ով կամ մասնաճյուղով և ավարտվում ստեղծման ամսաթվով։ Հսկայական ֆիլտրված աղյուսակների ճշգրիտ ընդհանուր count-երը թանկ են. որոշակի շեմից հետո ցույց տվեք «շատ արդյունքներ» կամ մոտավոր թիվ, կամ հաշվեք ասինխրոն։ Offset pagination-ը խորը էջերում վատանում է, որովհետև database-ը միևնույն է անցնում է բաց թողնված տողերով, իսկ keyset (cursor) pagination-ը մնում է արագ։ Խուսափեք N+1 query-ներից՝ eager-load անելով սյունակներում ցուցադրվող relation-ները։ Բազմաթիվ տեքստային սյունակներով որոնումը պետք է լինի search engine-ում կամ full-text index-ում, ոչ թե LIKE-երի շղթաներում։ Եվ վերջապես, ծանր dashboard-ները դրեք read replica-ի կամ ամփոփ աղյուսակների վրա, որպեսզի հաշվետվությունները թարմացնող մենեջերը երբեք չդանդաղեցնի checkout-ը։
- Log արեք admin-ի դանդաղ query-ները և վերանայեք դրանք ամեն ամիս։
- Export-ների և infinite scroll-ի համար նախընտրեք cursor pagination։
- Eager-load արեք ցուցակի սյունակներում օգտագործվող relation-ները։
10Admin մակերեսի պաշտպանություն
Admin-ը բիզնես մտնելու ամենահզոր դուռն է, ուստի արժանի է ամենաուժեղ կողպեքներին։ Յուրաքանչյուր admin account-ի համար պահանջեք երկրորդ factor։ Admin session-ները պահեք կարճ՝ idle timeout-ով, և կրիտիկական գործողություններից առաջ, ինչպիսիք են դերերի կամ payout-ի տվյալների փոփոխությունը, պահանջեք կրկնակի նույնականացում։ Մտածեք admin-ը առանձին subdomain-ով մատուցելու մասին՝ ավելի խիստ cookie-ներով և, որտեղ գործնական է, IP allowlist-ով կամ VPN-ով։ Պաշտպանվեք CSRF-ից cookie-ի վրա հիմնված session-ներում և rich-text դաշտերից եկող stored XSS-ից՝ HTML-ը սերվերում sanitize անելով tag-երի allowlist-ով։ Upload արված ֆայլերը երբեք չպետք է կատարելի լինեն. պահեք դրանք web root-ից դուրս կամ object storage-ում և օգտատերերի upload արած SVG-ն մատուցեք զգուշությամբ, որովհետև SVG-ն կարող է script-ներ պարունակել։ Հետևեք admin login-ներին և alert ստացեք նոր սարքերի կամ impossible travel-ի դեպքում։
Իրական իրավիճակներ
Ո՞վ է փոխել գինը
Խնդիր
Ամենավաճառվող ապրանքներից մեկը մեկ շաբաթ վաճառվել է կես գնով։ Երեք մարդ ունի admin հասանելիություն, ոչ ոք չի հիշում, որ խմբագրել է այն, և audit log չկա։
Ինչպես լուծել
Այս միջադեպի համար ստուգեք այն, ինչ կա. web server-ի log-երը՝ ապրանքի endpoint-ին ուղղված POST հարցումների համար, database-ի updated_at-ը, ERP sync-ի log-երը և գների import-ի պատմությունը։ Ապա կանխեք հաջորդը. service layer-ում ավելացրեք audit log, որը գրանցում է կատարողին, գործողությունը, արժեքները՝ առաջ և հետո, request ID-ն և աղբյուրը (էկրան, import կամ sync)։ Ընդհանուր admin դերը բաժանեք իրական դերերի, որպեսզի միայն pricing թիմը կարողանա թարմացնել գները, և որոշակի շեմից բարձր գնային փոփոխությունների համար պահանջեք պատճառի դաշտ։ Ավելացրեք ամենամեծ գնային փոփոխությունների օրական հաշվետվություն և sanity կանոն, որը 30%-ից ավելի փոփոխությունները պահում է հաստատման համար։
Պատվերների ցուցակը բեռնվում է 12 վայրկյանում
Խնդիր
Օպերատորները բողոքում են, որ պիկային ժամին պատվերների էկրանը անօգտագործելի է։ Աղյուսակն ունի երեք միլիոն տող, և ցուցակը բեռնվում է status ֆիլտրով, որոնման դաշտով և ընդհանուր count-ով։
Ինչպես լուծել
Նախ չափեք. միացրեք query logging կամ EXPLAIN ANALYZE ցուցակի query-ի համար։ Տիպիկ ուղղումներ՝ հերթականությամբ. ավելացրեք composite index, որը համապատասխանում է default ֆիլտրին և sort-ին, օրինակ՝ (status, created_at DESC), հեռացրեք ճշգրիտ count-ը կամ սահմանափակեք այն շեմով, eager-load արեք հաճախորդի և մասնաճյուղի relation-ները՝ N+1 query-ները հեռացնելու համար, մի քանի սյունակով LIKE որոնումը փոխարինեք հատուկ search դաշտով կամ full-text index-ով, անցեք keyset pagination-ի։ Ցուցակը default-ով ցույց տվեք վերջին 30 օրվա համար, ինչը համապատասխանում է օպերատորների կարիքներին։ Յուրաքանչյուր փոփոխությունից հետո նորից չափեք և query-ն ավելացրեք slow-query alert-ին։
Marketing-ը ուզում է landing page-եր առանց developer-ների
Խնդիր
Արշավի յուրաքանչյուր landing page պահանջում է, որ developer-ը կառուցի և deploy անի այն, ինչը տևում է մեկ շաբաթ։ Marketing-ը ուզում է էջերն ինքնուրույն հրապարակել, բայց դիզայն թիմը վախենում է կոտրված layout-ներից։
Ինչպես լուծել
Կառուցեք block-ների վրա հիմնված page builder. developer-ները ստեղծում են typed block-ների գրադարան՝ hero, product grid, banner, FAQ, տեքստ նկարով, յուրաքանչյուրը՝ դաշտերի schema-ով և վալիդացված սահմաններով, որոնք render են արվում design system-ին հետևող component-ներով։ Խմբագիրները էջերը կազմում են block-ներից, վերադասավորում դրանք, պլանավորում հրապարակումը և preview անում մինչև live գնալը։ Product grid-երը հղում են իրական ապրանքներին կամ կատեգորիաներին ըստ ID-ի, ուստի գներն ու մնացորդը մնում են live։ Յուրաքանչյուր էջ version արեք, որպեսզի խմբագիրները կարողանան rollback անել։ Սա պահում է դիզայնը միասնական, քանի որ խմբագիրներն ընտրում են բովանդակություն, ոչ թե CSS, իսկ նոր block type-եր կարելի է ավելացնել, երբ marketing-ին գրադարանն այլևս բավական չէ։
Արագ ուղեցույց
- Permission middleware route-ի վրա (Laravel)
Route::post('/admin/orders/{order}/refund', RefundOrder::class)->middleware('can:refund,order');- Super-admin bypass մեկ տեղում
Gate::before(fn (Admin $a) => $a->hasRole('super-admin') ? true : null);- Նկարների upload-ի վալիդացիա
'logo' => ['required', File::image()->max(5 * 1024)->dimensions(Rule::dimensions()->maxWidth(4000))]- Կարճաժամկետ private download հղում
Storage::disk('s3')->temporaryUrl($path, now()->addMinutes(15));- Մեծ export-ի stream
Order::query()->where(...)->lazyById(1000)->each(fn ($o) => $writer->addRow($o->toExportRow()));- Keyset pagination
Order::orderByDesc('id')->cursorPaginate(50);- Ցուցակների դանդաղ query-ների որոնում (PostgreSQL)
EXPLAIN (ANALYZE, BUFFERS) SELECT ... FROM orders WHERE status = 'paid' ORDER BY created_at DESC LIMIT 50;- Append-only audit աղյուսակ
REVOKE UPDATE, DELETE ON audit_logs FROM app_user;
Իմ իրական նախագծերում
- Ես սպասարկում եմ ներքին React ադմին համակարգ՝ որպես package-ներ․ config-driven CRUD ռեսուրսներ, մեդիա մենեջեր, մոտ 20 design-system կոմպոնենտ և CKEditor, media, slug և tree դաշտեր, Laravel-յան զույգով, որն առաջարկում է OTP ադմին login, դերեր, ընդհանուր resource controller և մեդիա գրադարան։
- Ռեստորանային ցանցի պլատֆորմում ադմինն ունի 12 դեր, ադմինի web push, մեդիա գրադարան՝ թղթապանակներով, crop-ով և փոխարինումով, SEO և sitemap ըստ մոդելի, մոտ 30 հաշվետվության controller և մոտ 40 Excel export endpoint։
- Գործակալության կայքում կառուցեցի հիբրիդ CMS՝ կառուցվածքային տվյալներ Laravel ադմինում և խմբագրական, բլոկային կոնտենտ headless CMS-ում․ այդ ձեռքով կառուցված ադմինը դարձավ reusable ադմին package-ների նախահայրը։
Հարցեր և պատասխաններ
Q1Ինչպե՞ս եք նախագծում ադմինի դերերն ու թույլտվությունները։
Թույլտվությունները սահմանում եմ որպես ռեսուրս-գործողություն զույգեր, օրինակ՝ orders.view, orders.refund կամ products.update, և խմբավորում դրանք դերերի մեջ, որոնք համապատասխանում են իրական աշխատանքներին՝ օպերատոր, կոնտենտի խմբագիր, մարքեթոլոգ, մենեջեր։ Սերվերը թույլտվություն է ստուգում յուրաքանչյուր route-ում, իսկ frontend-ը կարդում է նույն ցուցակը՝ մենյուներն ու կոճակները ցուցադրելու համար։ Super-admin-ը վերապահված է մի քանի մարդու։ Զգայուն գործողությունները ստանում են լրացուցիչ վերահսկողություն, օրինակ՝ պարտադիր պատճառ, audit logging և երբեմն երկրորդ հաստատող։ Custom դերերը թույլ են տալիս բիզնեսին հարմարվել առանց կոդի փոփոխությունների։
Q2Ի՞նչ է config-driven ադմինը և ե՞րբ է այն արժե։
Յուրաքանչյուր ցուցակ և ձև ձեռքով գրելու փոխարեն նկարագրում եք ռեսուրսը՝ endpoint, սյունակներ, ֆիլտրեր, դաշտեր և թույլտվություններ, և ընդհանուր շարժիչը ցուցադրում է էկրանները։ Այն արժե, երբ ունեք շատ նմանատիպ ռեսուրսներ կամ մի քանի նախագիծ, որովհետև յուրաքանչյուր նոր ռեսուրս րոպեներ է պահանջում և վարվում է հետևողականորեն։ Այն ամեն ինչի փոխարինող չէ․ բարդ workflow-ներին, օրինակ՝ պատվերների մշակմանը կամ ակցիաների կառուցիչին, դեռ պետք են custom էկրաններ, ուստի համակարգը պետք է աջակցի custom կոմպոնենտների միացմանը։
Q3Headless CMS, թե՞ ներկառուցված CMS։
Ներկառուցված CMS-ը կոնտենտը պահում է ձեր սեփական տվյալների բազայում՝ ապրանքների և պատվերների կողքին, ինչը պարզ է, անվճար և իդեալական, երբ էջերը խառնում են commerce տվյալներ։ Headless CMS-ը առանձին ծառայություն է՝ ուժեղ խմբագրական փորձով, preview-ներով, տեղայնացմամբ և workflow-ներով, որը մատուցվում է API-ով։ Ներկառուցվածն ընտրում եմ այն խանութների համար, որտեղ կոնտենտը հիմնականում բաններներ, էջեր և բլոգ է, իսկ headless-ը՝ երբ մարքեթինգի թիմը հաճախ է հրապարակում հարուստ բլոկային կոնտենտ։ Երկուսի հիբրիդը տարածված է և լավ է աշխատում։
Q4Ինչո՞ւ է պետք audit log։
Որովհետև թիմում «ո՞վ է փոխել այս գինը» կամ «ո՞վ է վերադարձրել այդ պատվերը» հարցերը անպայման կտրվեն, հաճախ՝ incident-ի ժամանակ։ Audit log-ը գրանցում է հեղինակին, գործողությունը, թիրախային գրառումը, առաջ և հետո արժեքները, ժամանակը և IP-ն։ Այն աջակցում է հաշվետվողականությանը, օգնում է support թիմերին պատասխանել հաճախորդներին, զսպում է չարաշահումները և հաճախ պահանջվում է compliance-ի համար։ Այն պետք է լինի append-only ադմինների համար, որոնելի՝ ըստ գրառման, և պահվի սահմանված ժամկետով։ Service layer-ում log անելը բռնում է փոփոխությունները յուրաքանչյուր էկրանից և API-ից։
Q5Ինչպե՞ս եք մշակում մեծ export-ները։
Երբեք վեբ հարցման մեջ։ Ադմինը սեղմում է export, սերվերը queue է անում job՝ ֆիլտրերով, և job-ը տողերը chunk-երով stream է անում տվյալների բազայից CSV կամ Excel ֆայլի մեջ՝ հիշողությունը հարթ պահելով։ Ավարտելուն պես ֆայլը պահում է private և օգտատիրոջը ծանուցում կարճաժամկետ ներբեռնման հղումով։ Export-ները պարունակում են անձնական և ֆինանսական տվյալներ, ուստի պահանջում են թույլտվություն, գրանցվում են audit trail-ում, իսկ ֆայլերը ջնջվում են մի քանի օր անց։
Սենիոր մակարդակի հարցեր
S1Կառուցե՞լ custom admin, օգտագործե՞լ Filament-ի նման framework, թե՞ low-code գործիք։
Կախված է նրանից, թե որքան կենտրոնական է admin-ը և քանի նախագիծ է այն կիսում։ Low-code գործիքները ամենաարագն են գոյություն ունեցող տվյալների վրա ներքին dashboard-ների համար, բայց դժվարանում են բարդ workflow-ների, custom permission-ների և branding-ի հետ և ավելացնում են յուրաքանչյուր օգտատիրոջ համար ծախս։ Filament-ի կամ Nova-ի նման framework-ները մեկ stack-ի ներսում տալիս են CRUD-ի հիանալի արագություն, փոխարենը պետք է հետևել դրանց կոնվենցիաներին և upgrade-ի ցիկլերին։ Custom, config-driven admin-ը սկզբում ամենաթանկն է, բայց իրեն արդարացնում է, երբ շատ նախագծերի պետք են նույն pattern-ները, երբ admin-ը պետք է աշխատի մի քանի backend-ի հետ, կամ երբ UX-ը product feature է, օրինակ՝ ռեստորանների օպերատորների համար։ Մեկ նախագծի համար ես կընտրեի framework-ը, իսկ custom համակարգ՝ միայն այն դեպքում, երբ նախագծերի միջև վերօգտագործումն ապացուցված է։
S2Ինչպե՞ս եք նախագծում audit համակարգ, որը չի դանդաղեցնում գրառումները և չի պայթում չափերով։
Կրիտիկական ֆինանսական գործողությունների համար audit գրառումները գրեք նույն transaction-ում, որովհետև դրանց կորուստն անընդունելի է, բայց պահեք դրանք փոքր. փոխված դաշտերի diff, ոչ թե ամբողջ տողեր։ Մեծ ծավալի, ցածր ռիսկի փոփոխությունների համար audit event-ները ուղարկեք queue կամ օգտագործեք database-ի change data capture՝ ընդունելով փոքր ուշացում։ Audit աղյուսակը partition արեք ըստ ամիսների, որպեսզի query-ները մնան արագ, իսկ retention-ը լինի հին partition-ների էժան drop, հնարավոր է՝ object storage-ում արխիվացնելուց հետո։ Index ստեղծեք ըստ subject-ի և ըստ actor-ի, ինչը ծածկում է հարցերի մեծ մասը։ Բացառեք աղմկոտ դաշտերը, ինչպիսիք են updated_at-ը կամ դիտումների counter-ները։ Trade-off-ը ամբողջականության և ծախսի միջև է, ուստի դասակարգեք գործողությունները. ֆինանսական և permission-ների փոփոխությունները միշտ log են արվում սինխրոն, մնացած ամեն ինչ՝ best-effort pipeline-ով։
S3Ինչպե՞ս կավելացնեիք multi-tenancy գոյություն ունեցող admin-ին։
Նախ որոշեք isolation-ի մոդելը։ Row-level tenancy-ն՝ tenant_id սյունակ յուրաքանչյուր աղյուսակում, ամենաէժանն է և ամենահեշտը շահագործման համար, բայց մեկ մոռացված ֆիլտրը տվյալներ է արտահոսեցնում, ուստի այն կիրառեք կենտրոնացված՝ global query scope-ով, կամ PostgreSQL-ի row-level security-ով՝ որպես պաշտպանության երկրորդ գիծ։ Schema-per-tenant-ը կամ database-per-tenant-ը տալիս են ավելի ուժեղ isolation և ավելի պարզ backup-ներ յուրաքանչյուր tenant-ի համար, բայց գնով՝ migration-ներ բազմաթիվ schema-ների վրա և ավելի բարդ cross-tenant հաշվետվություններ։ Admin-ում tenant-ը գալիս է նույնականացված օգտատիրոջից, երբեք՝ request-ի պարամետրից. super-admin-ները tenant-ը փոխում են բացահայտ, և այդ փոխումը audit է արվում։ Media-ին, cache-երին, queue-երին և search index-ներին նույնպես պետք են tenant prefix-ներ։ Ես ավելացնում եմ ավտոմատ թեստեր, որոնք յուրաքանչյուր endpoint-ով փորձում են կարդալ այլ tenant-ի գրառումները։
Բառարան
- CRUD
- Create, read, update, delete. չորս հիմնական գործողությունները, որոնք տրամադրում են admin էկրանների մեծ մասը։
- RBAC
- Role-based access control. permission-ները տրվում են դերերին, իսկ դերերը նշանակվում են օգտատերերին։
- ABAC
- Attribute-based access control. կանոններ, որոնք օգտագործում են նաև օգտատիրոջ և գրառման ատրիբուտները, օրինակ՝ մասնաճյուղը։
- Policy
- Class կամ ֆունկցիա, որը որոշում է՝ արդյոք օգտատերը կարող է գործողություն կատարել կոնկրետ գրառման վրա։
- Audit log
- Append-only գրառում այն մասին, թե ով ինչ արեց, երբ, որ գրառման հետ՝ արժեքներով առաջ և հետո։
- Keyset pagination
- Էջավորում վերջին տեսած key-ով offset-ի փոխարեն, որպեսզի խորը էջերը մնան արագ։
- Headless CMS
- Բովանդակության service, որը պահում է բովանդակությունը և մատուցում այն API-ով՝ առանց սեփական front end-ի։
- Content block
- Էջի բովանդակության typed, բազմակի օգտագործվող մաս՝ սահմանված schema-ով, որը render է արվում component-ով։
- Stored XSS
- Բովանդակության մեջ, օրինակ՝ rich text-ում, պահված վնասակար script, որը աշխատում է այլ օգտատերերի browser-ներում։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։