Բոլոր դասերը
Էլեկտրոնային առևտուրՄիջին11 րոպե ընթերցումԻմ ստեկում

Ադմին վահանակներ և CMS

Ինչ է պետք իրական back office-ին՝ դերեր, CRUD, մեդիա, SEO, հաշվետվություններ, export-ներ և audit log-եր։

  • Roles
  • CRUD
  • Media library
  • Reports
  • Exports
Քայլ 1/5
Խմբագիրը admin panel-ում նոր էջ է գրում։ Այն պահվում է որպես draft՝ կայքում դեռ հին banner-ն է։

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-ը գուշակությունը վերածում է երկու րոպեանոց որոնման։

php
// 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 համակարգը պետք է թույլ տա դրանք միացնել։

ts
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-ների վրա․ ավելի քիչ են հարմար հաճախորդներին ուղղված կոնտենտի համար։

Լավագույն պրակտիկաներ

  1. Թույլտվությունները պարտադրեք սերվերում յուրաքանչյուր endpoint-ի համար․ UI-ը միայն արտացոլում է դրանք։
  2. Ադմինի login-ի համար օգտագործեք երկրորդ գործոն կամ մեկանգամյա կոդեր։
  3. Զգայուն գործողությունները log արեք՝ առաջ/հետո արժեքներով, հեղինակով և պատճառով։
  4. Ստանդարտ CRUD-ը գեներացրեք կոնֆիգուրացիայից, իսկ custom էկրանները պահեք բարդ workflow-ների համար։
  5. Վերբեռնումները վալիդացրեք ըստ բովանդակության և նկարները մշակեք background job-երում։
  6. Յուրաքանչյուր հանրային մոդելի տվեք SEO դաշտեր և redirect-ները կառավարեք ադմինում։
  7. Ծանր հաշվետվություններն ու 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-երը լուռ են, մինչև տվյալներ չարտահոսեն։

php
// 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-ների և կարգավորումների համար և դրանք դարձրեք որոնելի՝ ըստ գրառման, մարդու և ժամանակահատվածի։

sql
-- 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-ի դեպքում։

Իրական իրավիճակներ

01

Ո՞վ է փոխել գինը

Խնդիր

Ամենավաճառվող ապրանքներից մեկը մեկ շաբաթ վաճառվել է կես գնով։ Երեք մարդ ունի 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%-ից ավելի փոփոխությունները պահում է հաստատման համար։

02

Պատվերների ցուցակը բեռնվում է 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-ին։

03

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-ներում։

Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։

Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։