Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s

This commit is contained in:
Matt
2026-02-14 15:26:42 +01:00
parent e56e143a40
commit b5425e705e
374 changed files with 116737 additions and 111969 deletions

View File

@@ -1,131 +1,131 @@
import type { StorageProvider, StorageProviderType } from './types'
import { S3StorageProvider } from './s3-provider'
import { LocalStorageProvider } from './local-provider'
import { prisma } from '@/lib/prisma'
export type { StorageProvider, StorageProviderType } from './types'
export { S3StorageProvider } from './s3-provider'
export { LocalStorageProvider } from './local-provider'
// Cached provider instance
let cachedProvider: StorageProvider | null = null
let cachedProviderType: StorageProviderType | null = null
/**
* Get the configured storage provider type from system settings
*/
async function getProviderTypeFromSettings(): Promise<StorageProviderType> {
try {
const setting = await prisma.systemSettings.findUnique({
where: { key: 'storage_provider' },
})
const value = setting?.value as StorageProviderType | undefined
return value === 'local' ? 'local' : 's3' // Default to S3
} catch {
// If settings table doesn't exist or error, default to S3
return 's3'
}
}
/**
* Get the current storage provider type from settings
*/
export async function getCurrentProviderType(): Promise<StorageProviderType> {
return getProviderTypeFromSettings()
}
/**
* Get a storage provider instance based on system settings
* Caches the provider for performance
*/
export async function getStorageProvider(): Promise<StorageProvider> {
const providerType = await getProviderTypeFromSettings()
// Return cached provider if type hasn't changed
if (cachedProvider && cachedProviderType === providerType) {
return cachedProvider
}
// Create new provider
if (providerType === 'local') {
cachedProvider = new LocalStorageProvider()
} else {
cachedProvider = new S3StorageProvider()
}
cachedProviderType = providerType
return cachedProvider
}
/**
* Get a storage provider and its type together
*/
export async function getStorageProviderWithType(): Promise<{
provider: StorageProvider
providerType: StorageProviderType
}> {
const providerType = await getProviderTypeFromSettings()
const provider = await getStorageProvider()
return { provider, providerType }
}
/**
* Create a specific storage provider (bypasses settings)
*/
export function createStorageProvider(type: StorageProviderType): StorageProvider {
if (type === 'local') {
return new LocalStorageProvider()
}
return new S3StorageProvider()
}
/**
* Clear the cached provider (call when settings change)
*/
export function clearStorageProviderCache(): void {
cachedProvider = null
cachedProviderType = null
}
/**
* Generate a unique storage key for avatars
*/
export function generateAvatarKey(userId: string, fileName: string): string {
const timestamp = Date.now()
const ext = fileName.split('.').pop() || 'jpg'
return `avatars/${userId}/${timestamp}.${ext}`
}
/**
* Generate a unique storage key for project logos
*/
export function generateLogoKey(projectId: string, fileName: string): string {
const timestamp = Date.now()
const ext = fileName.split('.').pop() || 'png'
return `logos/${projectId}/${timestamp}.${ext}`
}
/**
* Get content type from file extension
*/
export function getContentType(fileName: string): string {
const ext = fileName.toLowerCase().split('.').pop()
const types: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
pdf: 'application/pdf',
}
return types[ext || ''] || 'application/octet-stream'
}
/**
* Validate image file type
*/
export function isValidImageType(contentType: string): boolean {
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
return validTypes.includes(contentType)
}
import type { StorageProvider, StorageProviderType } from './types'
import { S3StorageProvider } from './s3-provider'
import { LocalStorageProvider } from './local-provider'
import { prisma } from '@/lib/prisma'
export type { StorageProvider, StorageProviderType } from './types'
export { S3StorageProvider } from './s3-provider'
export { LocalStorageProvider } from './local-provider'
// Cached provider instance
let cachedProvider: StorageProvider | null = null
let cachedProviderType: StorageProviderType | null = null
/**
* Get the configured storage provider type from system settings
*/
async function getProviderTypeFromSettings(): Promise<StorageProviderType> {
try {
const setting = await prisma.systemSettings.findUnique({
where: { key: 'storage_provider' },
})
const value = setting?.value as StorageProviderType | undefined
return value === 'local' ? 'local' : 's3' // Default to S3
} catch {
// If settings table doesn't exist or error, default to S3
return 's3'
}
}
/**
* Get the current storage provider type from settings
*/
export async function getCurrentProviderType(): Promise<StorageProviderType> {
return getProviderTypeFromSettings()
}
/**
* Get a storage provider instance based on system settings
* Caches the provider for performance
*/
export async function getStorageProvider(): Promise<StorageProvider> {
const providerType = await getProviderTypeFromSettings()
// Return cached provider if type hasn't changed
if (cachedProvider && cachedProviderType === providerType) {
return cachedProvider
}
// Create new provider
if (providerType === 'local') {
cachedProvider = new LocalStorageProvider()
} else {
cachedProvider = new S3StorageProvider()
}
cachedProviderType = providerType
return cachedProvider
}
/**
* Get a storage provider and its type together
*/
export async function getStorageProviderWithType(): Promise<{
provider: StorageProvider
providerType: StorageProviderType
}> {
const providerType = await getProviderTypeFromSettings()
const provider = await getStorageProvider()
return { provider, providerType }
}
/**
* Create a specific storage provider (bypasses settings)
*/
export function createStorageProvider(type: StorageProviderType): StorageProvider {
if (type === 'local') {
return new LocalStorageProvider()
}
return new S3StorageProvider()
}
/**
* Clear the cached provider (call when settings change)
*/
export function clearStorageProviderCache(): void {
cachedProvider = null
cachedProviderType = null
}
/**
* Generate a unique storage key for avatars
*/
export function generateAvatarKey(userId: string, fileName: string): string {
const timestamp = Date.now()
const ext = fileName.split('.').pop() || 'jpg'
return `avatars/${userId}/${timestamp}.${ext}`
}
/**
* Generate a unique storage key for project logos
*/
export function generateLogoKey(projectId: string, fileName: string): string {
const timestamp = Date.now()
const ext = fileName.split('.').pop() || 'png'
return `logos/${projectId}/${timestamp}.${ext}`
}
/**
* Get content type from file extension
*/
export function getContentType(fileName: string): string {
const ext = fileName.toLowerCase().split('.').pop()
const types: Record<string, string> = {
jpg: 'image/jpeg',
jpeg: 'image/jpeg',
png: 'image/png',
gif: 'image/gif',
webp: 'image/webp',
svg: 'image/svg+xml',
pdf: 'application/pdf',
}
return types[ext || ''] || 'application/octet-stream'
}
/**
* Validate image file type
*/
export function isValidImageType(contentType: string): boolean {
const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']
return validTypes.includes(contentType)
}