chore: telemetry localization (#6075)
This commit is contained in:
@@ -27,6 +27,7 @@ export const nodemailerAdapter = async (
|
||||
const { defaultFromAddress, defaultFromName, transport } = await buildEmail(args)
|
||||
|
||||
const adapter: NodemailerAdapter = () => ({
|
||||
name: 'nodemailer',
|
||||
defaultFromAddress,
|
||||
defaultFromName,
|
||||
sendEmail: async (message) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import { isPlainObject } from '../../utilities/isPlainObject.js'
|
||||
import baseVersionFields from '../../versions/baseFields.js'
|
||||
import { authDefaults, defaults } from './defaults.js'
|
||||
|
||||
const sanitizeCollection = (
|
||||
export const sanitizeCollection = (
|
||||
config: Config,
|
||||
collection: CollectionConfig,
|
||||
): SanitizedCollectionConfig => {
|
||||
@@ -159,5 +159,3 @@ const sanitizeCollection = (
|
||||
|
||||
return sanitized as SanitizedCollectionConfig
|
||||
}
|
||||
|
||||
export default sanitizeCollection
|
||||
|
||||
@@ -152,6 +152,7 @@ const collectionSchema = joi.object().keys({
|
||||
}),
|
||||
upload: joi.alternatives().try(
|
||||
joi.object({
|
||||
adapter: joi.string(),
|
||||
adminThumbnail: joi.alternatives().try(joi.string(), componentSchema),
|
||||
crop: joi.bool(),
|
||||
disableLocalStorage: joi.bool(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
} from './types.js'
|
||||
|
||||
import { defaultUserCollection } from '../auth/defaultUser.js'
|
||||
import sanitizeCollection from '../collections/config/sanitize.js'
|
||||
import { sanitizeCollection } from '../collections/config/sanitize.js'
|
||||
import { migrationsCollection } from '../database/migrations/migrationsCollection.js'
|
||||
import { InvalidConfiguration } from '../errors/index.js'
|
||||
import sanitizeGlobals from '../globals/config/sanitize.js'
|
||||
@@ -110,5 +110,11 @@ export const sanitizeConfig = (incomingConfig: Config): SanitizedConfig => {
|
||||
config.csrf.push(config.serverURL)
|
||||
}
|
||||
|
||||
// Get deduped list of upload adapters
|
||||
if (!config.upload) config.upload = { adapters: [] }
|
||||
config.upload.adapters = Array.from(
|
||||
new Set(config.collections.map((c) => c.upload?.adapter).filter(Boolean)),
|
||||
)
|
||||
|
||||
return config as SanitizedConfig
|
||||
}
|
||||
|
||||
@@ -640,7 +640,7 @@ export type Config = {
|
||||
|
||||
export type SanitizedConfig = Omit<
|
||||
DeepRequired<Config>,
|
||||
'collections' | 'endpoint' | 'globals' | 'i18n' | 'localization'
|
||||
'collections' | 'endpoint' | 'globals' | 'i18n' | 'localization' | 'upload'
|
||||
> & {
|
||||
collections: SanitizedCollectionConfig[]
|
||||
endpoints: Endpoint[]
|
||||
@@ -652,6 +652,12 @@ export type SanitizedConfig = Omit<
|
||||
configDir: string
|
||||
rawConfig: string
|
||||
}
|
||||
upload: ExpressFileUploadOptions & {
|
||||
/**
|
||||
* Deduped list of adapters used in the project
|
||||
*/
|
||||
adapters: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type EditConfig =
|
||||
|
||||
@@ -4,6 +4,7 @@ import { emailDefaults } from './defaults.js'
|
||||
import { getStringifiedToAddress } from './getStringifiedToAddress.js'
|
||||
|
||||
export const consoleEmailAdapter: EmailAdapter<void> = ({ payload }) => ({
|
||||
name: 'console',
|
||||
defaultFromAddress: emailDefaults.defaultFromAddress,
|
||||
defaultFromName: emailDefaults.defaultFromName,
|
||||
sendEmail: async (message) => {
|
||||
|
||||
@@ -27,5 +27,6 @@ export type InitializedEmailAdapter<TSendEmailResponse = unknown> = ReturnType<
|
||||
export type EmailAdapter<TSendEmailResponse = unknown> = ({ payload }: { payload: Payload }) => {
|
||||
defaultFromAddress: string
|
||||
defaultFromName: string
|
||||
name: string
|
||||
sendEmail: (message: SendEmailOptions) => Promise<TSendEmailResponse>
|
||||
}
|
||||
|
||||
@@ -70,6 +70,10 @@ export type ImageSize = Omit<ResizeOptions, 'withoutEnlargement'> & {
|
||||
export type GetAdminThumbnail = (args: { doc: Record<string, unknown> }) => false | null | string
|
||||
|
||||
export type UploadConfig = {
|
||||
/**
|
||||
* The adapter to use for uploads.
|
||||
*/
|
||||
adapter?: string
|
||||
/**
|
||||
* Represents an admin thumbnail, which can be either a React component or a string.
|
||||
* - If a string, it should be one of the image size names.
|
||||
|
||||
@@ -17,13 +17,18 @@ const Conf = (ConfImport.default || ConfImport) as unknown as typeof ConfImport.
|
||||
|
||||
export type BaseEvent = {
|
||||
ciName: null | string
|
||||
dbAdapter: string
|
||||
emailAdapter: null | string
|
||||
envID: string
|
||||
isCI: boolean
|
||||
locales: string[]
|
||||
localizationDefaultLocale: null | string
|
||||
localizationEnabled: boolean
|
||||
nodeEnv: string
|
||||
nodeVersion: string
|
||||
payloadPackages: Record<string, string>
|
||||
payloadVersion: string
|
||||
projectID: string
|
||||
uploadAdapters: string[]
|
||||
}
|
||||
|
||||
type PackageJSON = {
|
||||
@@ -42,7 +47,7 @@ let baseEvent: BaseEvent | null = null
|
||||
|
||||
export const sendEvent = async ({ event, payload }: Args): Promise<void> => {
|
||||
try {
|
||||
const packageJSON = await getPackageJSON()
|
||||
const { packageJSON, packageJSONPath } = await getPackageJSON()
|
||||
|
||||
// Only generate the base event once
|
||||
if (!baseEvent) {
|
||||
@@ -52,15 +57,18 @@ export const sendEvent = async ({ event, payload }: Args): Promise<void> => {
|
||||
isCI: ciInfo.isCI,
|
||||
nodeEnv: process.env.NODE_ENV || 'development',
|
||||
nodeVersion: process.version,
|
||||
payloadPackages: getPayloadPackages(packageJSON),
|
||||
payloadVersion: getPayloadVersion(packageJSON),
|
||||
projectID: getProjectID(payload, packageJSON),
|
||||
...getLocalizationInfo(payload),
|
||||
dbAdapter: payload.db.name,
|
||||
emailAdapter: payload.email?.name || null,
|
||||
uploadAdapters: payload.config.upload.adapters,
|
||||
}
|
||||
}
|
||||
|
||||
if (process.env.PAYLOAD_TELEMETRY_DEBUG) {
|
||||
payload.logger.info({
|
||||
event: { ...baseEvent, ...event },
|
||||
event: { ...baseEvent, ...event, packageJSONPath },
|
||||
msg: 'Telemetry Event',
|
||||
})
|
||||
return
|
||||
@@ -120,18 +128,23 @@ const getGitID = (payload: Payload) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getPayloadPackages = (packageJSON: PackageJSON): Record<string, string> => {
|
||||
return Object.keys(packageJSON.dependencies || {}).reduce((acc, key) => {
|
||||
return key.startsWith('@payloadcms/') ? { ...acc, [key]: packageJSON.dependencies[key] } : acc
|
||||
}, {})
|
||||
}
|
||||
const getPackageJSON = async (): Promise<{
|
||||
packageJSON?: PackageJSON
|
||||
packageJSONPath: string
|
||||
}> => {
|
||||
let packageJSONPath = path.resolve(process.cwd(), 'package.json')
|
||||
|
||||
const getPackageJSON = async (): Promise<PackageJSON> => {
|
||||
if (!fs.existsSync(packageJSONPath)) {
|
||||
// Old logic
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
const packageJsonPath = await findUp('package.json', { cwd: dirname })
|
||||
const jsonContent: PackageJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))
|
||||
return jsonContent
|
||||
packageJSONPath = await findUp('package.json', { cwd: dirname })
|
||||
const jsonContent: PackageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8'))
|
||||
return { packageJSON: jsonContent, packageJSONPath }
|
||||
}
|
||||
|
||||
const packageJSON: PackageJSON = JSON.parse(fs.readFileSync(packageJSONPath, 'utf-8'))
|
||||
return { packageJSON, packageJSONPath }
|
||||
}
|
||||
|
||||
const getPackageJSONID = (payload: Payload, packageJSON: PackageJSON): string => {
|
||||
@@ -141,3 +154,21 @@ const getPackageJSONID = (payload: Payload, packageJSON: PackageJSON): string =>
|
||||
export const getPayloadVersion = (packageJSON: PackageJSON): string => {
|
||||
return packageJSON?.dependencies?.payload ?? ''
|
||||
}
|
||||
|
||||
export const getLocalizationInfo = (
|
||||
payload: Payload,
|
||||
): Pick<BaseEvent, 'locales' | 'localizationDefaultLocale' | 'localizationEnabled'> => {
|
||||
if (!payload.config.localization) {
|
||||
return {
|
||||
locales: [],
|
||||
localizationDefaultLocale: null,
|
||||
localizationEnabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
locales: payload.config.localization.localeCodes,
|
||||
localizationDefaultLocale: payload.config.localization.defaultLocale,
|
||||
localizationEnabled: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ export const azureBlobStorageAdapter = ({
|
||||
|
||||
return ({ collection, prefix }): GeneratedAdapter => {
|
||||
return {
|
||||
name: 'azure',
|
||||
generateURL: getGenerateURL({ baseURL, containerName }),
|
||||
handleDelete: getHandleDelete({ collection, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -48,6 +48,7 @@ export const gcsAdapter =
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'gcs',
|
||||
generateURL: getGenerateURL({ bucket, getStorageClient }),
|
||||
handleDelete: getHandleDelete({ bucket, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -54,6 +54,7 @@ export const s3Adapter =
|
||||
}
|
||||
|
||||
return {
|
||||
name: 's3',
|
||||
generateURL: getGenerateURL({ bucket, config }),
|
||||
handleDelete: getHandleDelete({ bucket, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -72,6 +72,7 @@ export const vercelBlobAdapter =
|
||||
const baseUrl = `https://${storeId}.${access}.blob.vercel-storage.com`
|
||||
|
||||
return {
|
||||
name: 'vercel-blob',
|
||||
generateURL: getGenerateUrl({ baseUrl, prefix }),
|
||||
handleDelete: getHandleDelete({ baseUrl, prefix, token }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -25,10 +25,10 @@ export const getAfterDeleteHook = ({
|
||||
|
||||
await Promise.all(promises)
|
||||
} catch (err: unknown) {
|
||||
req.payload.logger.error(
|
||||
`There was an error while deleting files corresponding to the ${collection.labels?.singular} with ID ${doc.id}:`,
|
||||
)
|
||||
req.payload.logger.error(err)
|
||||
req.payload.logger.error({
|
||||
err,
|
||||
msg: `There was an error while deleting files corresponding to the ${collection.labels?.singular} with ID ${doc.id}.`,
|
||||
})
|
||||
}
|
||||
return doc
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ export const cloudStorage =
|
||||
},
|
||||
upload: {
|
||||
...(typeof existingCollection.upload === 'object' ? existingCollection.upload : {}),
|
||||
adapter: adapter.name,
|
||||
disableLocalStorage:
|
||||
typeof options.disableLocalStorage === 'boolean'
|
||||
? options.disableLocalStorage
|
||||
|
||||
@@ -43,6 +43,7 @@ export interface GeneratedAdapter {
|
||||
generateURL: GenerateURL
|
||||
handleDelete: HandleDelete
|
||||
handleUpload: HandleUpload
|
||||
name: string
|
||||
onInit?: () => void
|
||||
staticHandler: StaticHandler
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ function azureStorageInternal({
|
||||
|
||||
return ({ collection, prefix }): GeneratedAdapter => {
|
||||
return {
|
||||
name: 'azure',
|
||||
generateURL: getGenerateURL({ baseURL, containerName }),
|
||||
handleDelete: getHandleDelete({ collection, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -100,6 +100,7 @@ function gcsStorageInternal({ acl, bucket, options }: GcsStorageOptions): Adapte
|
||||
}
|
||||
|
||||
return {
|
||||
name: 'gcs',
|
||||
generateURL: getGenerateURL({ bucket, getStorageClient }),
|
||||
handleDelete: getHandleDelete({ bucket, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -112,6 +112,7 @@ function s3StorageInternal({ acl, bucket, config = {} }: S3StorageOptions): Adap
|
||||
}
|
||||
|
||||
return {
|
||||
name: 's3',
|
||||
generateURL: getGenerateURL({ bucket, config }),
|
||||
handleDelete: getHandleDelete({ bucket, getStorageClient }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
@@ -136,6 +136,7 @@ function vercelBlobStorageInternal(
|
||||
return ({ collection, prefix }): GeneratedAdapter => {
|
||||
const { access, addRandomSuffix, baseUrl, cacheControlMaxAge, token } = options
|
||||
return {
|
||||
name: 'vercel-blob',
|
||||
generateURL: getGenerateUrl({ baseUrl, prefix }),
|
||||
handleDelete: getHandleDelete({ baseUrl, prefix, token: options.token }),
|
||||
handleUpload: getHandleUpload({
|
||||
|
||||
Reference in New Issue
Block a user