Files
website-enchun-mgr/apps/backend/src/collections/Portfolio/index.ts
pkupuk 7fd73e0e3d Implement Sprint 1 stories: collections, RBAC, audit logging, load testing
Complete 6 Sprint 1 stories for Epic 1 web migration infrastructure.

Portfolio Collection:
- Add 7 fields: title, slug, url, image, description, websiteType, tags
- Configure R2 storage and authenticated access control

Categories Collection:
- Add nameEn, order, textColor, backgroundColor fields
- Add color picker UI configuration

Posts Collection:
- Add excerpt with 200 char limit and ogImage for social sharing
- Add showInFooter checkbox and status select (draft/review/published)

Role-Based Access Control:
- Add role field to Users collection (admin/editor)
- Create adminOnly and authenticated access functions
- Apply access rules to Portfolio, Categories, Posts, Users collections

Audit Logging System (NFR9):
- Create Audit collection with timestamps for 90-day retention
- Add auditLogger utility for login/logout/content change tracking
- Add auditChange and auditGlobalChange hooks to all collections and globals
- Add cleanupAuditLogs job with 90-day retention policy

Load Testing Framework (NFR4):
- Add k6 load testing with 3 scripts: public-browsing, admin-operations, api-performance
- Configure targets: p95 < 500ms, error rate < 1%, 100 concurrent users
- Add verification script and comprehensive documentation

Other Changes:
- Remove unused Form blocks
- Add Header/Footer audit hooks
- Regenerate Payload TypeScript types
2026-01-31 17:20:35 +08:00

81 lines
1.7 KiB
TypeScript

import type { CollectionConfig } from 'payload'
import { anyone } from '../../access/anyone'
import { authenticated } from '../../access/authenticated'
import { auditChange } from '../../collections/Audit/hooks/auditHooks'
import { slugField } from '@/fields/slug'
export const Portfolio: CollectionConfig = {
slug: 'portfolio',
access: {
create: authenticated,
read: anyone,
update: authenticated,
delete: authenticated,
},
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'websiteType', 'updatedAt'],
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'url',
type: 'text',
admin: {
description: 'Website URL (e.g., https://example.com)',
},
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
required: true,
admin: {
description: 'Preview image stored in R2',
},
},
{
name: 'description',
type: 'textarea',
},
{
name: 'websiteType',
type: 'select',
options: [
{ label: '企業官網', value: 'corporate' },
{ label: '電商網站', value: 'ecommerce' },
{ label: '活動頁面', value: 'landing' },
{ label: '品牌網站', value: 'brand' },
{ label: '其他', value: 'other' },
],
required: true,
},
{
name: 'tags',
type: 'array',
fields: [
{
name: 'tag',
type: 'text',
},
],
},
...slugField(),
],
hooks: {
afterChange: [auditChange('portfolio')],
afterDelete: [auditChange('portfolio')],
},
versions: {
drafts: {
autosave: true,
},
maxPerDoc: 10,
},
}