-
-
-
- {{ t('admin.accounts.selectAllowedModels') }}
-
-
-
-
-
-
-
-
+
{{ t('admin.accounts.selectedModels', { count: allowedModels.length }) }}
{{
@@ -1176,6 +1136,7 @@
import { ref, reactive, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
+import { claudeModels, getPresetMappingsByPlatform, getModelsByPlatform, commonErrorCodes, buildModelMappingObject } from '@/composables/useModelWhitelist'
import { useAuthStore } from '@/stores/auth'
import { adminAPI } from '@/api/admin'
import {
@@ -1190,6 +1151,7 @@ import type { Proxy, Group, AccountPlatform, AccountType } from '@/types'
import BaseDialog from '@/components/common/BaseDialog.vue'
import ProxySelector from '@/components/common/ProxySelector.vue'
import GroupSelector from '@/components/common/GroupSelector.vue'
+import ModelWhitelistSelector from '@/components/account/ModelWhitelistSelector.vue'
import OAuthAuthorizationFlow from './OAuthAuthorizationFlow.vue'
// Type for exposed OAuthAuthorizationFlow component
@@ -1302,178 +1264,8 @@ const mixedScheduling = ref(false) // For antigravity accounts: enable mixed sch
const geminiOAuthType = ref<'code_assist' | 'ai_studio'>('code_assist')
const geminiAIStudioOAuthEnabled = ref(false)
-// Common models for whitelist - Anthropic
-const anthropicModels = [
- { value: 'claude-opus-4-5-20251101', label: 'Claude Opus 4.5' },
- { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4' },
- { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5' },
- { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku' },
- { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5' },
- { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' },
- { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' },
- { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }
-]
-
-// Common models for whitelist - OpenAI
-const openaiModels = [
- { value: 'gpt-5.2-2025-12-11', label: 'GPT-5.2' },
- { value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
- { value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
- { value: 'gpt-5.1-codex', label: 'GPT-5.1 Codex' },
- { value: 'gpt-5.1-2025-11-13', label: 'GPT-5.1' },
- { value: 'gpt-5.1-codex-mini', label: 'GPT-5.1 Codex Mini' },
- { value: 'gpt-5-2025-08-07', label: 'GPT-5' }
-]
-
-// Common models for whitelist - Gemini
-const geminiModels = [
- { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' },
- { value: 'gemini-2.0-flash-lite', label: 'Gemini 2.0 Flash Lite' },
- { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro' },
- { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash' }
-]
-
-// Computed: current models based on platform
-const commonModels = computed(() => {
- if (form.platform === 'openai') return openaiModels
- if (form.platform === 'gemini') return geminiModels
- return anthropicModels
-})
-
-// Preset mappings for quick add - Anthropic
-const anthropicPresetMappings = [
- {
- label: 'Sonnet 4',
- from: 'claude-sonnet-4-20250514',
- to: 'claude-sonnet-4-20250514',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'Sonnet 4.5',
- from: 'claude-sonnet-4-5-20250929',
- to: 'claude-sonnet-4-5-20250929',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: 'Opus 4.5',
- from: 'claude-opus-4-5-20251101',
- to: 'claude-opus-4-5-20251101',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: 'Haiku 3.5',
- from: 'claude-3-5-haiku-20241022',
- to: 'claude-3-5-haiku-20241022',
- color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400'
- },
- {
- label: 'Haiku 4.5',
- from: 'claude-haiku-4-5-20251001',
- to: 'claude-haiku-4-5-20251001',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- },
- {
- label: 'Opus->Sonnet',
- from: 'claude-opus-4-5-20251101',
- to: 'claude-sonnet-4-5-20250929',
- color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400'
- }
-]
-
-// Preset mappings for quick add - OpenAI
-const openaiPresetMappings = [
- {
- label: 'GPT-5.2',
- from: 'gpt-5.2-2025-12-11',
- to: 'gpt-5.2-2025-12-11',
- color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400'
- },
- {
- label: 'GPT-5.2 Codex',
- from: 'gpt-5.2-codex',
- to: 'gpt-5.2-codex',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'GPT-5.1 Codex',
- from: 'gpt-5.1-codex',
- to: 'gpt-5.1-codex',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: 'Codex Max',
- from: 'gpt-5.1-codex-max',
- to: 'gpt-5.1-codex-max',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: 'Codex Mini',
- from: 'gpt-5.1-codex-mini',
- to: 'gpt-5.1-codex-mini',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- },
- {
- label: 'Max->Codex',
- from: 'gpt-5.1-codex-max',
- to: 'gpt-5.1-codex',
- color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400'
- }
-]
-
-// Preset mappings for quick add - Gemini
-const geminiPresetMappings = [
- {
- label: 'Flash',
- from: 'gemini-2.0-flash',
- to: 'gemini-2.0-flash',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'Flash Lite',
- from: 'gemini-2.0-flash-lite',
- to: 'gemini-2.0-flash-lite',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: '1.5 Pro',
- from: 'gemini-1.5-pro',
- to: 'gemini-1.5-pro',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: '1.5 Flash',
- from: 'gemini-1.5-flash',
- to: 'gemini-1.5-flash',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- }
-]
-
// Computed: current preset mappings based on platform
-const presetMappings = computed(() => {
- if (form.platform === 'openai') return openaiPresetMappings
- if (form.platform === 'gemini') return geminiPresetMappings
- return anthropicPresetMappings
-})
-
-// Common HTTP error codes for quick selection
-const commonErrorCodes = [
- { value: 401, label: 'Unauthorized' },
- { value: 403, label: 'Forbidden' },
- { value: 429, label: 'Rate Limit' },
- { value: 500, label: 'Server Error' },
- { value: 502, label: 'Bad Gateway' },
- { value: 503, label: 'Unavailable' },
- { value: 529, label: 'Overloaded' }
-]
+const presetMappings = computed(() => getPresetMappingsByPlatform(form.platform))
const form = reactive({
name: '',
@@ -1511,7 +1303,10 @@ const canExchangeCode = computed(() => {
watch(
() => props.show,
(newVal) => {
- if (!newVal) {
+ if (newVal) {
+ // Modal opened - fill related models
+ allowedModels.value = [...getModelsByPlatform(form.platform)]
+ } else {
resetForm()
}
}
@@ -1585,6 +1380,16 @@ const handleSelectGeminiOAuthType = (oauthType: 'code_assist' | 'ai_studio') =>
geminiOAuthType.value = oauthType
}
+// Auto-fill related models when switching to whitelist mode or changing platform
+watch(
+ [modelRestrictionMode, () => form.platform],
+ ([newMode]) => {
+ if (newMode === 'whitelist') {
+ allowedModels.value = [...getModelsByPlatform(form.platform)]
+ }
+ }
+)
+
// Model mapping helpers
const addModelMapping = () => {
modelMappings.value.push({ from: '', to: '' })
@@ -1595,9 +1400,7 @@ const removeModelMapping = (index: number) => {
}
const addPresetMapping = (from: string, to: string) => {
- // Check if mapping already exists
- const exists = modelMappings.value.some((m) => m.from === from)
- if (exists) {
+ if (modelMappings.value.some((m) => m.from === from)) {
appStore.showInfo(t('admin.accounts.mappingExists', { model: from }))
return
}
@@ -1637,28 +1440,6 @@ const removeErrorCode = (code: number) => {
}
}
-const buildModelMappingObject = (): Record | null => {
- const mapping: Record = {}
-
- if (modelRestrictionMode.value === 'whitelist') {
- // Whitelist mode: map model to itself
- for (const model of allowedModels.value) {
- mapping[model] = model
- }
- } else {
- // Mapping mode: use custom mappings
- for (const m of modelMappings.value) {
- const from = m.from.trim()
- const to = m.to.trim()
- if (from && to) {
- mapping[from] = to
- }
- }
- }
-
- return Object.keys(mapping).length > 0 ? mapping : null
-}
-
// Methods
const resetForm = () => {
step.value = 1
@@ -1676,7 +1457,7 @@ const resetForm = () => {
apiKeyValue.value = ''
modelMappings.value = []
modelRestrictionMode.value = 'whitelist'
- allowedModels.value = []
+ allowedModels.value = [...claudeModels] // Default fill related models
customErrorCodesEnabled.value = false
selectedErrorCodes.value = []
customErrorCodeInput.value = null
@@ -1725,7 +1506,7 @@ const handleSubmit = async () => {
}
// Add model mapping if configured
- const modelMapping = buildModelMappingObject()
+ const modelMapping = buildModelMappingObject(modelRestrictionMode.value, allowedModels.value, modelMappings.value)
if (modelMapping) {
credentials.model_mapping = modelMapping
}
diff --git a/frontend/src/components/account/EditAccountModal.vue b/frontend/src/components/account/EditAccountModal.vue
index 32a36c77..39280e2c 100644
--- a/frontend/src/components/account/EditAccountModal.vue
+++ b/frontend/src/components/account/EditAccountModal.vue
@@ -111,47 +111,7 @@
-
-
-
- {{ t('admin.accounts.selectAllowedModels') }}
-
-
-
-
-
-
-
-
+
{{ t('admin.accounts.selectedModels', { count: allowedModels.length }) }}
{{
@@ -565,6 +525,12 @@ import BaseDialog from '@/components/common/BaseDialog.vue'
import Select from '@/components/common/Select.vue'
import ProxySelector from '@/components/common/ProxySelector.vue'
import GroupSelector from '@/components/common/GroupSelector.vue'
+import ModelWhitelistSelector from '@/components/account/ModelWhitelistSelector.vue'
+import {
+ getPresetMappingsByPlatform,
+ commonErrorCodes,
+ buildModelMappingObject
+} from '@/composables/useModelWhitelist'
interface Props {
show: boolean
@@ -610,167 +576,8 @@ const customErrorCodeInput = ref(null)
const interceptWarmupRequests = ref(false)
const mixedScheduling = ref(false) // For antigravity accounts: enable mixed scheduling
-// Common models for whitelist - Anthropic
-const anthropicModels = [
- { value: 'claude-opus-4-5-20251101', label: 'Claude Opus 4.5' },
- { value: 'claude-sonnet-4-20250514', label: 'Claude Sonnet 4' },
- { value: 'claude-sonnet-4-5-20250929', label: 'Claude Sonnet 4.5' },
- { value: 'claude-3-5-haiku-20241022', label: 'Claude 3.5 Haiku' },
- { value: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5' },
- { value: 'claude-3-opus-20240229', label: 'Claude 3 Opus' },
- { value: 'claude-3-5-sonnet-20241022', label: 'Claude 3.5 Sonnet' },
- { value: 'claude-3-haiku-20240307', label: 'Claude 3 Haiku' }
-]
-
-// Common models for whitelist - OpenAI
-const openaiModels = [
- { value: 'gpt-5.2-2025-12-11', label: 'GPT-5.2' },
- { value: 'gpt-5.2-codex', label: 'GPT-5.2 Codex' },
- { value: 'gpt-5.1-codex-max', label: 'GPT-5.1 Codex Max' },
- { value: 'gpt-5.1-codex', label: 'GPT-5.1 Codex' },
- { value: 'gpt-5.1-2025-11-13', label: 'GPT-5.1' },
- { value: 'gpt-5.1-codex-mini', label: 'GPT-5.1 Codex Mini' },
- { value: 'gpt-5-2025-08-07', label: 'GPT-5' }
-]
-
-// Common models for whitelist - Gemini
-const geminiModels = [
- { value: 'gemini-2.0-flash', label: 'Gemini 2.0 Flash' },
- { value: 'gemini-2.0-flash-lite', label: 'Gemini 2.0 Flash Lite' },
- { value: 'gemini-1.5-pro', label: 'Gemini 1.5 Pro' },
- { value: 'gemini-1.5-flash', label: 'Gemini 1.5 Flash' }
-]
-
-// Computed: current models based on platform
-const commonModels = computed(() => {
- if (props.account?.platform === 'openai') return openaiModels
- if (props.account?.platform === 'gemini') return geminiModels
- return anthropicModels
-})
-
-// Preset mappings for quick add - Anthropic
-const anthropicPresetMappings = [
- {
- label: 'Sonnet 4',
- from: 'claude-sonnet-4-20250514',
- to: 'claude-sonnet-4-20250514',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'Sonnet 4.5',
- from: 'claude-sonnet-4-5-20250929',
- to: 'claude-sonnet-4-5-20250929',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: 'Opus 4.5',
- from: 'claude-opus-4-5-20251101',
- to: 'claude-opus-4-5-20251101',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: 'Haiku 3.5',
- from: 'claude-3-5-haiku-20241022',
- to: 'claude-3-5-haiku-20241022',
- color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400'
- },
- {
- label: 'Haiku 4.5',
- from: 'claude-haiku-4-5-20251001',
- to: 'claude-haiku-4-5-20251001',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- },
- {
- label: 'Opus->Sonnet',
- from: 'claude-opus-4-5-20251101',
- to: 'claude-sonnet-4-5-20250929',
- color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400'
- }
-]
-
-// Preset mappings for quick add - OpenAI
-const openaiPresetMappings = [
- {
- label: 'GPT-5.2',
- from: 'gpt-5.2-2025-12-11',
- to: 'gpt-5.2-2025-12-11',
- color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400'
- },
- {
- label: 'GPT-5.2 Codex',
- from: 'gpt-5.2-codex',
- to: 'gpt-5.2-codex',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'GPT-5.1 Codex',
- from: 'gpt-5.1-codex',
- to: 'gpt-5.1-codex',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: 'Codex Max',
- from: 'gpt-5.1-codex-max',
- to: 'gpt-5.1-codex-max',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: 'Codex Mini',
- from: 'gpt-5.1-codex-mini',
- to: 'gpt-5.1-codex-mini',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- },
- {
- label: 'Max->Codex',
- from: 'gpt-5.1-codex-max',
- to: 'gpt-5.1-codex',
- color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400'
- }
-]
-
-// Preset mappings for quick add - Gemini
-const geminiPresetMappings = [
- {
- label: 'Flash',
- from: 'gemini-2.0-flash',
- to: 'gemini-2.0-flash',
- color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400'
- },
- {
- label: 'Flash Lite',
- from: 'gemini-2.0-flash-lite',
- to: 'gemini-2.0-flash-lite',
- color:
- 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400'
- },
- {
- label: '1.5 Pro',
- from: 'gemini-1.5-pro',
- to: 'gemini-1.5-pro',
- color:
- 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400'
- },
- {
- label: '1.5 Flash',
- from: 'gemini-1.5-flash',
- to: 'gemini-1.5-flash',
- color:
- 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400'
- }
-]
-
// Computed: current preset mappings based on platform
-const presetMappings = computed(() => {
- if (props.account?.platform === 'openai') return openaiPresetMappings
- if (props.account?.platform === 'gemini') return geminiPresetMappings
- return anthropicPresetMappings
-})
+const presetMappings = computed(() => getPresetMappingsByPlatform(props.account?.platform || 'anthropic'))
// Computed: default base URL based on platform
const defaultBaseUrl = computed(() => {
@@ -779,17 +586,6 @@ const defaultBaseUrl = computed(() => {
return 'https://api.anthropic.com'
})
-// Common HTTP error codes for quick selection
-const commonErrorCodes = [
- { value: 401, label: 'Unauthorized' },
- { value: 403, label: 'Forbidden' },
- { value: 429, label: 'Rate Limit' },
- { value: 500, label: 'Server Error' },
- { value: 502, label: 'Bad Gateway' },
- { value: 503, label: 'Unavailable' },
- { value: 529, label: 'Overloaded' }
-]
-
const form = reactive({
name: '',
proxy_id: null as number | null,
@@ -940,28 +736,6 @@ const removeErrorCode = (code: number) => {
}
}
-const buildModelMappingObject = (): Record | null => {
- const mapping: Record = {}
-
- if (modelRestrictionMode.value === 'whitelist') {
- // Whitelist mode: model maps to itself
- for (const model of allowedModels.value) {
- mapping[model] = model
- }
- } else {
- // Mapping mode: use the mapping entries
- for (const m of modelMappings.value) {
- const from = m.from.trim()
- const to = m.to.trim()
- if (from && to) {
- mapping[from] = to
- }
- }
- }
-
- return Object.keys(mapping).length > 0 ? mapping : null
-}
-
// Methods
const handleClose = () => {
emit('close')
@@ -978,7 +752,7 @@ const handleSubmit = async () => {
if (props.account.type === 'apikey') {
const currentCredentials = (props.account.credentials as Record) || {}
const newBaseUrl = editBaseUrl.value.trim() || defaultBaseUrl.value
- const modelMapping = buildModelMappingObject()
+ const modelMapping = buildModelMappingObject(modelRestrictionMode.value, allowedModels.value, modelMappings.value)
// Always update credentials for apikey type to handle model mapping changes
const newCredentials: Record = {
diff --git a/frontend/src/components/account/ModelWhitelistSelector.vue b/frontend/src/components/account/ModelWhitelistSelector.vue
new file mode 100644
index 00000000..b029d376
--- /dev/null
+++ b/frontend/src/components/account/ModelWhitelistSelector.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+ {{ model }}
+
+
+
+
+
+
{{ t('admin.accounts.modelCount', { count: modelValue.length }) }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('admin.accounts.noMatchingModels') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/common/ModelIcon.vue b/frontend/src/components/common/ModelIcon.vue
new file mode 100644
index 00000000..2a05bf71
--- /dev/null
+++ b/frontend/src/components/common/ModelIcon.vue
@@ -0,0 +1,278 @@
+
+
+
+ {{ fallbackText }}
+
+
+
+
+
+
diff --git a/frontend/src/composables/useModelWhitelist.ts b/frontend/src/composables/useModelWhitelist.ts
new file mode 100644
index 00000000..d18bdc5f
--- /dev/null
+++ b/frontend/src/composables/useModelWhitelist.ts
@@ -0,0 +1,299 @@
+// =====================
+// 模型列表(硬编码,与 new-api 一致)
+// =====================
+
+// OpenAI
+const openaiModels = [
+ 'gpt-3.5-turbo', 'gpt-3.5-turbo-0125', 'gpt-3.5-turbo-1106', 'gpt-3.5-turbo-16k',
+ 'gpt-4', 'gpt-4-turbo', 'gpt-4-turbo-preview',
+ 'gpt-4o', 'gpt-4o-2024-08-06', 'gpt-4o-2024-11-20',
+ 'gpt-4o-mini', 'gpt-4o-mini-2024-07-18',
+ 'gpt-4.5-preview',
+ 'gpt-4.1', 'gpt-4.1-mini', 'gpt-4.1-nano',
+ 'o1', 'o1-preview', 'o1-mini', 'o1-pro',
+ 'o3', 'o3-mini', 'o3-pro',
+ 'o4-mini',
+ 'gpt-5', 'gpt-5-mini', 'gpt-5-nano',
+ 'chatgpt-4o-latest',
+ 'gpt-4o-audio-preview', 'gpt-4o-realtime-preview'
+]
+
+// Anthropic Claude
+export const claudeModels = [
+ 'claude-3-5-sonnet-20241022', 'claude-3-5-sonnet-20240620',
+ 'claude-3-5-haiku-20241022',
+ 'claude-3-opus-20240229', 'claude-3-sonnet-20240229', 'claude-3-haiku-20240307',
+ 'claude-3-7-sonnet-20250219',
+ 'claude-sonnet-4-20250514', 'claude-opus-4-20250514',
+ 'claude-opus-4-1-20250805',
+ 'claude-sonnet-4-5-20250929', 'claude-haiku-4-5-20251001',
+ 'claude-opus-4-5-20251101',
+ 'claude-2.1', 'claude-2.0', 'claude-instant-1.2'
+]
+
+// Google Gemini
+const geminiModels = [
+ 'gemini-2.0-flash', 'gemini-2.0-flash-lite-preview', 'gemini-2.0-flash-exp',
+ 'gemini-2.0-pro-exp', 'gemini-2.0-flash-thinking-exp',
+ 'gemini-2.5-pro-exp-03-25', 'gemini-2.5-pro-preview-03-25',
+ 'gemini-3-pro-preview',
+ 'gemini-1.5-pro', 'gemini-1.5-pro-latest',
+ 'gemini-1.5-flash', 'gemini-1.5-flash-latest', 'gemini-1.5-flash-8b',
+ 'gemini-exp-1206'
+]
+
+// 智谱 GLM
+const zhipuModels = [
+ 'glm-4', 'glm-4v', 'glm-4-plus', 'glm-4-0520',
+ 'glm-4-air', 'glm-4-airx', 'glm-4-long', 'glm-4-flash',
+ 'glm-4v-plus', 'glm-4.5', 'glm-4.6',
+ 'glm-3-turbo', 'glm-4-alltools',
+ 'chatglm_turbo', 'chatglm_pro', 'chatglm_std', 'chatglm_lite',
+ 'cogview-3', 'cogvideo'
+]
+
+// 阿里 通义千问
+const qwenModels = [
+ 'qwen-turbo', 'qwen-plus', 'qwen-max', 'qwen-max-longcontext', 'qwen-long',
+ 'qwen2-72b-instruct', 'qwen2-57b-a14b-instruct', 'qwen2-7b-instruct',
+ 'qwen2.5-72b-instruct', 'qwen2.5-32b-instruct', 'qwen2.5-14b-instruct',
+ 'qwen2.5-7b-instruct', 'qwen2.5-3b-instruct', 'qwen2.5-1.5b-instruct',
+ 'qwen2.5-coder-32b-instruct', 'qwen2.5-coder-14b-instruct', 'qwen2.5-coder-7b-instruct',
+ 'qwen3-235b-a22b',
+ 'qwq-32b', 'qwq-32b-preview'
+]
+
+// DeepSeek
+const deepseekModels = [
+ 'deepseek-chat', 'deepseek-coder', 'deepseek-reasoner',
+ 'deepseek-v3', 'deepseek-v3-0324',
+ 'deepseek-r1', 'deepseek-r1-0528',
+ 'deepseek-r1-distill-qwen-32b', 'deepseek-r1-distill-qwen-14b', 'deepseek-r1-distill-qwen-7b',
+ 'deepseek-r1-distill-llama-70b', 'deepseek-r1-distill-llama-8b'
+]
+
+// Mistral
+const mistralModels = [
+ 'mistral-small-latest', 'mistral-medium-latest', 'mistral-large-latest',
+ 'open-mistral-7b', 'open-mixtral-8x7b', 'open-mixtral-8x22b',
+ 'codestral-latest', 'codestral-mamba',
+ 'pixtral-12b-2409', 'pixtral-large-latest'
+]
+
+// Meta Llama
+const metaModels = [
+ 'llama-3.3-70b-instruct',
+ 'llama-3.2-90b-vision-instruct', 'llama-3.2-11b-vision-instruct',
+ 'llama-3.2-3b-instruct', 'llama-3.2-1b-instruct',
+ 'llama-3.1-405b-instruct', 'llama-3.1-70b-instruct', 'llama-3.1-8b-instruct',
+ 'llama-3-70b-instruct', 'llama-3-8b-instruct',
+ 'codellama-70b-instruct', 'codellama-34b-instruct', 'codellama-13b-instruct'
+]
+
+// xAI Grok
+const xaiModels = [
+ 'grok-4', 'grok-4-0709',
+ 'grok-3-beta', 'grok-3-mini-beta', 'grok-3-fast-beta',
+ 'grok-2', 'grok-2-vision', 'grok-2-image',
+ 'grok-beta', 'grok-vision-beta'
+]
+
+// Cohere
+const cohereModels = [
+ 'command-a-03-2025',
+ 'command-r', 'command-r-plus',
+ 'command-r-08-2024', 'command-r-plus-08-2024',
+ 'c4ai-aya-23-35b', 'c4ai-aya-23-8b',
+ 'command', 'command-light'
+]
+
+// Yi (01.AI)
+const yiModels = [
+ 'yi-large', 'yi-large-turbo', 'yi-large-rag',
+ 'yi-medium', 'yi-medium-200k',
+ 'yi-spark', 'yi-vision',
+ 'yi-1.5-34b-chat', 'yi-1.5-9b-chat', 'yi-1.5-6b-chat'
+]
+
+// Moonshot/Kimi
+const moonshotModels = [
+ 'moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k',
+ 'kimi-latest'
+]
+
+// 字节跳动 豆包
+const doubaoModels = [
+ 'doubao-pro-256k', 'doubao-pro-128k', 'doubao-pro-32k', 'doubao-pro-4k',
+ 'doubao-lite-128k', 'doubao-lite-32k', 'doubao-lite-4k',
+ 'doubao-vision-pro-32k', 'doubao-vision-lite-32k',
+ 'doubao-1.5-pro-256k', 'doubao-1.5-pro-32k', 'doubao-1.5-lite-32k',
+ 'doubao-1.5-pro-vision-32k', 'doubao-1.5-thinking-pro'
+]
+
+// MiniMax
+const minimaxModels = [
+ 'abab6.5-chat', 'abab6.5s-chat', 'abab6.5s-chat-pro',
+ 'abab6-chat',
+ 'abab5.5-chat', 'abab5.5s-chat'
+]
+
+// 百度 文心
+const baiduModels = [
+ 'ernie-4.0-8k-latest', 'ernie-4.0-8k', 'ernie-4.0-turbo-8k',
+ 'ernie-3.5-8k', 'ernie-3.5-128k',
+ 'ernie-speed-8k', 'ernie-speed-128k', 'ernie-speed-pro-128k',
+ 'ernie-lite-8k', 'ernie-lite-pro-128k',
+ 'ernie-tiny-8k'
+]
+
+// 讯飞 星火
+const sparkModels = [
+ 'spark-desk', 'spark-desk-v1.1', 'spark-desk-v2.1',
+ 'spark-desk-v3.1', 'spark-desk-v3.5', 'spark-desk-v4.0',
+ 'spark-lite', 'spark-pro', 'spark-max', 'spark-ultra'
+]
+
+// 腾讯 混元
+const hunyuanModels = [
+ 'hunyuan-lite', 'hunyuan-standard', 'hunyuan-standard-256k',
+ 'hunyuan-pro', 'hunyuan-turbo', 'hunyuan-large',
+ 'hunyuan-vision', 'hunyuan-code'
+]
+
+// Perplexity
+const perplexityModels = [
+ 'sonar', 'sonar-pro', 'sonar-reasoning',
+ 'llama-3-sonar-small-32k-online', 'llama-3-sonar-large-32k-online',
+ 'llama-3-sonar-small-32k-chat', 'llama-3-sonar-large-32k-chat'
+]
+
+// 所有模型(去重)
+const allModelsList: string[] = [
+ ...openaiModels,
+ ...claudeModels,
+ ...geminiModels,
+ ...zhipuModels,
+ ...qwenModels,
+ ...deepseekModels,
+ ...mistralModels,
+ ...metaModels,
+ ...xaiModels,
+ ...cohereModels,
+ ...yiModels,
+ ...moonshotModels,
+ ...doubaoModels,
+ ...minimaxModels,
+ ...baiduModels,
+ ...sparkModels,
+ ...hunyuanModels,
+ ...perplexityModels
+]
+
+// 转换为下拉选项格式
+export const allModels = allModelsList.map(m => ({ value: m, label: m }))
+
+// =====================
+// 预设映射
+// =====================
+
+const anthropicPresetMappings = [
+ { label: 'Sonnet 4', from: 'claude-sonnet-4-20250514', to: 'claude-sonnet-4-20250514', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' },
+ { label: 'Sonnet 4.5', from: 'claude-sonnet-4-5-20250929', to: 'claude-sonnet-4-5-20250929', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
+ { label: 'Opus 4.5', from: 'claude-opus-4-5-20251101', to: 'claude-opus-4-5-20251101', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
+ { label: 'Haiku 3.5', from: 'claude-3-5-haiku-20241022', to: 'claude-3-5-haiku-20241022', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' },
+ { label: 'Haiku 4.5', from: 'claude-haiku-4-5-20251001', to: 'claude-haiku-4-5-20251001', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' },
+ { label: 'Opus->Sonnet', from: 'claude-opus-4-5-20251101', to: 'claude-sonnet-4-5-20250929', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }
+]
+
+const openaiPresetMappings = [
+ { label: 'GPT-4o', from: 'gpt-4o', to: 'gpt-4o', color: 'bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400' },
+ { label: 'GPT-4o Mini', from: 'gpt-4o-mini', to: 'gpt-4o-mini', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' },
+ { label: 'GPT-4.1', from: 'gpt-4.1', to: 'gpt-4.1', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
+ { label: 'o1', from: 'o1', to: 'o1', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
+ { label: 'o3', from: 'o3', to: 'o3', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' },
+ { label: 'GPT-5', from: 'gpt-5', to: 'gpt-5', color: 'bg-amber-100 text-amber-700 hover:bg-amber-200 dark:bg-amber-900/30 dark:text-amber-400' }
+]
+
+const geminiPresetMappings = [
+ { label: 'Flash 2.0', from: 'gemini-2.0-flash', to: 'gemini-2.0-flash', color: 'bg-blue-100 text-blue-700 hover:bg-blue-200 dark:bg-blue-900/30 dark:text-blue-400' },
+ { label: 'Flash Lite', from: 'gemini-2.0-flash-lite-preview', to: 'gemini-2.0-flash-lite-preview', color: 'bg-indigo-100 text-indigo-700 hover:bg-indigo-200 dark:bg-indigo-900/30 dark:text-indigo-400' },
+ { label: '1.5 Pro', from: 'gemini-1.5-pro', to: 'gemini-1.5-pro', color: 'bg-purple-100 text-purple-700 hover:bg-purple-200 dark:bg-purple-900/30 dark:text-purple-400' },
+ { label: '1.5 Flash', from: 'gemini-1.5-flash', to: 'gemini-1.5-flash', color: 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-400' }
+]
+
+// =====================
+// 常用错误码
+// =====================
+
+export const commonErrorCodes = [
+ { value: 401, label: 'Unauthorized' },
+ { value: 403, label: 'Forbidden' },
+ { value: 429, label: 'Rate Limit' },
+ { value: 500, label: 'Server Error' },
+ { value: 502, label: 'Bad Gateway' },
+ { value: 503, label: 'Unavailable' },
+ { value: 529, label: 'Overloaded' }
+]
+
+// =====================
+// 辅助函数
+// =====================
+
+// 按平台获取模型
+export function getModelsByPlatform(platform: string): string[] {
+ switch (platform) {
+ case 'openai': return openaiModels
+ case 'anthropic':
+ case 'claude': return claudeModels
+ case 'gemini': return geminiModels
+ case 'zhipu': return zhipuModels
+ case 'qwen': return qwenModels
+ case 'deepseek': return deepseekModels
+ case 'mistral': return mistralModels
+ case 'meta': return metaModels
+ case 'xai': return xaiModels
+ case 'cohere': return cohereModels
+ case 'yi': return yiModels
+ case 'moonshot': return moonshotModels
+ case 'doubao': return doubaoModels
+ case 'minimax': return minimaxModels
+ case 'baidu': return baiduModels
+ case 'spark': return sparkModels
+ case 'hunyuan': return hunyuanModels
+ case 'perplexity': return perplexityModels
+ default: return claudeModels
+ }
+}
+
+// 按平台获取预设映射
+export function getPresetMappingsByPlatform(platform: string) {
+ if (platform === 'openai') return openaiPresetMappings
+ if (platform === 'gemini') return geminiPresetMappings
+ return anthropicPresetMappings
+}
+
+// =====================
+// 构建模型映射对象(用于 API)
+// =====================
+
+export function buildModelMappingObject(
+ mode: 'whitelist' | 'mapping',
+ allowedModels: string[],
+ modelMappings: { from: string; to: string }[]
+): Record | null {
+ const mapping: Record = {}
+
+ if (mode === 'whitelist') {
+ for (const model of allowedModels) {
+ mapping[model] = model
+ }
+ } else {
+ for (const m of modelMappings) {
+ const from = m.from.trim()
+ const to = m.to.trim()
+ if (from && to) mapping[from] = to
+ }
+ }
+
+ return Object.keys(mapping).length > 0 ? mapping : null
+}
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index d153b553..6bfdb54d 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -944,6 +944,15 @@ export default {
actualModel: 'Actual model',
addMapping: 'Add Mapping',
mappingExists: 'Mapping for {model} already exists',
+ searchModels: 'Search models...',
+ noMatchingModels: 'No matching models',
+ fillRelatedModels: 'Fill related models',
+ clearAllModels: 'Clear all models',
+ customModelName: 'Custom model name',
+ enterCustomModelName: 'Enter custom model name',
+ addModel: 'Add',
+ modelExists: 'Model already exists',
+ modelCount: '{count} models',
customErrorCodes: 'Custom Error Codes',
customErrorCodesHint: 'Only stop scheduling for selected error codes',
customErrorCodesWarning:
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index c6105683..218f7de3 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -1093,6 +1093,15 @@ export default {
actualModel: '实际模型',
addMapping: '添加映射',
mappingExists: '模型 {model} 的映射已存在',
+ searchModels: '搜索模型...',
+ noMatchingModels: '没有匹配的模型',
+ fillRelatedModels: '填入相关模型',
+ clearAllModels: '清除所有模型',
+ customModelName: '自定义模型名称',
+ enterCustomModelName: '输入自定义模型名称',
+ addModel: '填入',
+ modelExists: '该模型已存在',
+ modelCount: '{count} 个模型',
customErrorCodes: '自定义错误码',
customErrorCodesHint: '仅对选中的错误码停止调度',
customErrorCodesWarning: '仅选中的错误码会停止调度,其他错误将返回 500。',
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
index 4b6a33ba..a1731cfb 100644
--- a/frontend/tsconfig.json
+++ b/frontend/tsconfig.json
@@ -17,7 +17,8 @@
"noFallthroughCasesInSwitch": true,
"paths": {
"@/*": ["./src/*"]
- }
+ },
+ "types": ["vite/client"]
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
From 3c3fed886fff516ae9c864e043786595397f6966 Mon Sep 17 00:00:00 2001
From: Edric Li
Date: Thu, 1 Jan 2026 18:58:34 +0800
Subject: [PATCH 40/51] feat(backend): add user custom attributes system
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add a flexible user attribute system that allows admins to define
custom fields for users (text, textarea, number, email, url, date,
select, multi_select types).
- Add Ent schemas for UserAttributeDefinition and UserAttributeValue
- Add service layer with validation logic
- Add repository layer with batch operations support
- Add admin API endpoints for CRUD and reorder operations
- Add batch API for loading attribute values for multiple users
- Add database migration (018_user_attributes.sql)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5
---
backend/cmd/server/wire_gen.go | 12 +-
backend/ent/client.go | 412 +++-
backend/ent/ent.go | 26 +-
backend/ent/hook/hook.go | 24 +
backend/ent/intercept/intercept.go | 60 +
backend/ent/migrate/schema.go | 97 +-
backend/ent/mutation.go | 1970 ++++++++++++++++-
backend/ent/predicate/predicate.go | 6 +
backend/ent/runtime/runtime.go | 132 +-
.../ent/schema/user_attribute_definition.go | 109 +
backend/ent/schema/user_attribute_value.go | 74 +
backend/ent/tx.go | 6 +
backend/ent/userattributedefinition.go | 276 +++
.../userattributedefinition.go | 205 ++
backend/ent/userattributedefinition/where.go | 664 ++++++
backend/ent/userattributedefinition_create.go | 1267 +++++++++++
backend/ent/userattributedefinition_delete.go | 88 +
backend/ent/userattributedefinition_query.go | 606 +++++
backend/ent/userattributedefinition_update.go | 846 +++++++
backend/ent/userattributevalue.go | 198 ++
.../userattributevalue/userattributevalue.go | 139 ++
backend/ent/userattributevalue/where.go | 327 +++
backend/ent/userattributevalue_create.go | 731 ++++++
backend/ent/userattributevalue_delete.go | 88 +
backend/ent/userattributevalue_query.go | 681 ++++++
backend/ent/userattributevalue_update.go | 504 +++++
.../handler/admin/user_attribute_handler.go | 342 +++
backend/internal/handler/handler.go | 1 +
backend/internal/handler/wire.go | 3 +
.../repository/user_attribute_repo.go | 387 ++++
backend/internal/repository/wire.go | 2 +
backend/internal/server/routes/admin.go | 19 +
backend/internal/service/user_attribute.go | 125 ++
.../service/user_attribute_service.go | 295 +++
backend/internal/service/wire.go | 1 +
backend/migrations/018_user_attributes.sql | 48 +
36 files changed, 10649 insertions(+), 122 deletions(-)
create mode 100644 backend/ent/schema/user_attribute_definition.go
create mode 100644 backend/ent/schema/user_attribute_value.go
create mode 100644 backend/ent/userattributedefinition.go
create mode 100644 backend/ent/userattributedefinition/userattributedefinition.go
create mode 100644 backend/ent/userattributedefinition/where.go
create mode 100644 backend/ent/userattributedefinition_create.go
create mode 100644 backend/ent/userattributedefinition_delete.go
create mode 100644 backend/ent/userattributedefinition_query.go
create mode 100644 backend/ent/userattributedefinition_update.go
create mode 100644 backend/ent/userattributevalue.go
create mode 100644 backend/ent/userattributevalue/userattributevalue.go
create mode 100644 backend/ent/userattributevalue/where.go
create mode 100644 backend/ent/userattributevalue_create.go
create mode 100644 backend/ent/userattributevalue_delete.go
create mode 100644 backend/ent/userattributevalue_query.go
create mode 100644 backend/ent/userattributevalue_update.go
create mode 100644 backend/internal/handler/admin/user_attribute_handler.go
create mode 100644 backend/internal/repository/user_attribute_repo.go
create mode 100644 backend/internal/service/user_attribute.go
create mode 100644 backend/internal/service/user_attribute_service.go
create mode 100644 backend/migrations/018_user_attributes.sql
diff --git a/backend/cmd/server/wire_gen.go b/backend/cmd/server/wire_gen.go
index ad74d184..91569497 100644
--- a/backend/cmd/server/wire_gen.go
+++ b/backend/cmd/server/wire_gen.go
@@ -114,15 +114,19 @@ func initializeApplication(buildInfo handler.BuildInfo) (*Application, error) {
gitHubReleaseClient := repository.NewGitHubReleaseClient()
serviceBuildInfo := provideServiceBuildInfo(buildInfo)
updateService := service.ProvideUpdateService(updateCache, gitHubReleaseClient, serviceBuildInfo)
+ systemHandler := handler.ProvideSystemHandler(updateService)
+ adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService)
+ adminUsageHandler := admin.NewUsageHandler(usageService, apiKeyService, adminService)
+ userAttributeDefinitionRepository := repository.NewUserAttributeDefinitionRepository(client)
+ userAttributeValueRepository := repository.NewUserAttributeValueRepository(client, db)
+ userAttributeService := service.NewUserAttributeService(userAttributeDefinitionRepository, userAttributeValueRepository)
+ userAttributeHandler := admin.NewUserAttributeHandler(userAttributeService)
+ adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, settingHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler, userAttributeHandler)
pricingRemoteClient := repository.NewPricingRemoteClient()
pricingService, err := service.ProvidePricingService(configConfig, pricingRemoteClient)
if err != nil {
return nil, err
}
- systemHandler := handler.ProvideSystemHandler(updateService)
- adminSubscriptionHandler := admin.NewSubscriptionHandler(subscriptionService)
- adminUsageHandler := admin.NewUsageHandler(usageService, apiKeyService, adminService)
- adminHandlers := handler.ProvideAdminHandlers(dashboardHandler, adminUserHandler, groupHandler, accountHandler, oAuthHandler, openAIOAuthHandler, geminiOAuthHandler, antigravityOAuthHandler, proxyHandler, adminRedeemHandler, settingHandler, systemHandler, adminSubscriptionHandler, adminUsageHandler)
billingService := service.NewBillingService(configConfig, pricingService)
identityCache := repository.NewIdentityCache(redisClient)
identityService := service.NewIdentityService(identityCache)
diff --git a/backend/ent/client.go b/backend/ent/client.go
index 909226fa..fab70489 100644
--- a/backend/ent/client.go
+++ b/backend/ent/client.go
@@ -25,6 +25,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
stdsql "database/sql"
@@ -55,6 +57,10 @@ type Client struct {
User *UserClient
// UserAllowedGroup is the client for interacting with the UserAllowedGroup builders.
UserAllowedGroup *UserAllowedGroupClient
+ // UserAttributeDefinition is the client for interacting with the UserAttributeDefinition builders.
+ UserAttributeDefinition *UserAttributeDefinitionClient
+ // UserAttributeValue is the client for interacting with the UserAttributeValue builders.
+ UserAttributeValue *UserAttributeValueClient
// UserSubscription is the client for interacting with the UserSubscription builders.
UserSubscription *UserSubscriptionClient
}
@@ -78,6 +84,8 @@ func (c *Client) init() {
c.UsageLog = NewUsageLogClient(c.config)
c.User = NewUserClient(c.config)
c.UserAllowedGroup = NewUserAllowedGroupClient(c.config)
+ c.UserAttributeDefinition = NewUserAttributeDefinitionClient(c.config)
+ c.UserAttributeValue = NewUserAttributeValueClient(c.config)
c.UserSubscription = NewUserSubscriptionClient(c.config)
}
@@ -169,19 +177,21 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
cfg := c.config
cfg.driver = tx
return &Tx{
- ctx: ctx,
- config: cfg,
- Account: NewAccountClient(cfg),
- AccountGroup: NewAccountGroupClient(cfg),
- ApiKey: NewApiKeyClient(cfg),
- Group: NewGroupClient(cfg),
- Proxy: NewProxyClient(cfg),
- RedeemCode: NewRedeemCodeClient(cfg),
- Setting: NewSettingClient(cfg),
- UsageLog: NewUsageLogClient(cfg),
- User: NewUserClient(cfg),
- UserAllowedGroup: NewUserAllowedGroupClient(cfg),
- UserSubscription: NewUserSubscriptionClient(cfg),
+ ctx: ctx,
+ config: cfg,
+ Account: NewAccountClient(cfg),
+ AccountGroup: NewAccountGroupClient(cfg),
+ ApiKey: NewApiKeyClient(cfg),
+ Group: NewGroupClient(cfg),
+ Proxy: NewProxyClient(cfg),
+ RedeemCode: NewRedeemCodeClient(cfg),
+ Setting: NewSettingClient(cfg),
+ UsageLog: NewUsageLogClient(cfg),
+ User: NewUserClient(cfg),
+ UserAllowedGroup: NewUserAllowedGroupClient(cfg),
+ UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
+ UserAttributeValue: NewUserAttributeValueClient(cfg),
+ UserSubscription: NewUserSubscriptionClient(cfg),
}, nil
}
@@ -199,19 +209,21 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{
- ctx: ctx,
- config: cfg,
- Account: NewAccountClient(cfg),
- AccountGroup: NewAccountGroupClient(cfg),
- ApiKey: NewApiKeyClient(cfg),
- Group: NewGroupClient(cfg),
- Proxy: NewProxyClient(cfg),
- RedeemCode: NewRedeemCodeClient(cfg),
- Setting: NewSettingClient(cfg),
- UsageLog: NewUsageLogClient(cfg),
- User: NewUserClient(cfg),
- UserAllowedGroup: NewUserAllowedGroupClient(cfg),
- UserSubscription: NewUserSubscriptionClient(cfg),
+ ctx: ctx,
+ config: cfg,
+ Account: NewAccountClient(cfg),
+ AccountGroup: NewAccountGroupClient(cfg),
+ ApiKey: NewApiKeyClient(cfg),
+ Group: NewGroupClient(cfg),
+ Proxy: NewProxyClient(cfg),
+ RedeemCode: NewRedeemCodeClient(cfg),
+ Setting: NewSettingClient(cfg),
+ UsageLog: NewUsageLogClient(cfg),
+ User: NewUserClient(cfg),
+ UserAllowedGroup: NewUserAllowedGroupClient(cfg),
+ UserAttributeDefinition: NewUserAttributeDefinitionClient(cfg),
+ UserAttributeValue: NewUserAttributeValueClient(cfg),
+ UserSubscription: NewUserSubscriptionClient(cfg),
}, nil
}
@@ -242,7 +254,8 @@ func (c *Client) Close() error {
func (c *Client) Use(hooks ...Hook) {
for _, n := range []interface{ Use(...Hook) }{
c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting,
- c.UsageLog, c.User, c.UserAllowedGroup, c.UserSubscription,
+ c.UsageLog, c.User, c.UserAllowedGroup, c.UserAttributeDefinition,
+ c.UserAttributeValue, c.UserSubscription,
} {
n.Use(hooks...)
}
@@ -253,7 +266,8 @@ func (c *Client) Use(hooks ...Hook) {
func (c *Client) Intercept(interceptors ...Interceptor) {
for _, n := range []interface{ Intercept(...Interceptor) }{
c.Account, c.AccountGroup, c.ApiKey, c.Group, c.Proxy, c.RedeemCode, c.Setting,
- c.UsageLog, c.User, c.UserAllowedGroup, c.UserSubscription,
+ c.UsageLog, c.User, c.UserAllowedGroup, c.UserAttributeDefinition,
+ c.UserAttributeValue, c.UserSubscription,
} {
n.Intercept(interceptors...)
}
@@ -282,6 +296,10 @@ func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
return c.User.mutate(ctx, m)
case *UserAllowedGroupMutation:
return c.UserAllowedGroup.mutate(ctx, m)
+ case *UserAttributeDefinitionMutation:
+ return c.UserAttributeDefinition.mutate(ctx, m)
+ case *UserAttributeValueMutation:
+ return c.UserAttributeValue.mutate(ctx, m)
case *UserSubscriptionMutation:
return c.UserSubscription.mutate(ctx, m)
default:
@@ -1916,6 +1934,22 @@ func (c *UserClient) QueryUsageLogs(_m *User) *UsageLogQuery {
return query
}
+// QueryAttributeValues queries the attribute_values edge of a User.
+func (c *UserClient) QueryAttributeValues(_m *User) *UserAttributeValueQuery {
+ query := (&UserAttributeValueClient{config: c.config}).Query()
+ query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+ id := _m.ID
+ step := sqlgraph.NewStep(
+ sqlgraph.From(user.Table, user.FieldID, id),
+ sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, user.AttributeValuesTable, user.AttributeValuesColumn),
+ )
+ fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
+ return fromV, nil
+ }
+ return query
+}
+
// QueryUserAllowedGroups queries the user_allowed_groups edge of a User.
func (c *UserClient) QueryUserAllowedGroups(_m *User) *UserAllowedGroupQuery {
query := (&UserAllowedGroupClient{config: c.config}).Query()
@@ -2075,6 +2109,322 @@ func (c *UserAllowedGroupClient) mutate(ctx context.Context, m *UserAllowedGroup
}
}
+// UserAttributeDefinitionClient is a client for the UserAttributeDefinition schema.
+type UserAttributeDefinitionClient struct {
+ config
+}
+
+// NewUserAttributeDefinitionClient returns a client for the UserAttributeDefinition from the given config.
+func NewUserAttributeDefinitionClient(c config) *UserAttributeDefinitionClient {
+ return &UserAttributeDefinitionClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `userattributedefinition.Hooks(f(g(h())))`.
+func (c *UserAttributeDefinitionClient) Use(hooks ...Hook) {
+ c.hooks.UserAttributeDefinition = append(c.hooks.UserAttributeDefinition, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `userattributedefinition.Intercept(f(g(h())))`.
+func (c *UserAttributeDefinitionClient) Intercept(interceptors ...Interceptor) {
+ c.inters.UserAttributeDefinition = append(c.inters.UserAttributeDefinition, interceptors...)
+}
+
+// Create returns a builder for creating a UserAttributeDefinition entity.
+func (c *UserAttributeDefinitionClient) Create() *UserAttributeDefinitionCreate {
+ mutation := newUserAttributeDefinitionMutation(c.config, OpCreate)
+ return &UserAttributeDefinitionCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of UserAttributeDefinition entities.
+func (c *UserAttributeDefinitionClient) CreateBulk(builders ...*UserAttributeDefinitionCreate) *UserAttributeDefinitionCreateBulk {
+ return &UserAttributeDefinitionCreateBulk{config: c.config, builders: builders}
+}
+
+// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
+// a builder and applies setFunc on it.
+func (c *UserAttributeDefinitionClient) MapCreateBulk(slice any, setFunc func(*UserAttributeDefinitionCreate, int)) *UserAttributeDefinitionCreateBulk {
+ rv := reflect.ValueOf(slice)
+ if rv.Kind() != reflect.Slice {
+ return &UserAttributeDefinitionCreateBulk{err: fmt.Errorf("calling to UserAttributeDefinitionClient.MapCreateBulk with wrong type %T, need slice", slice)}
+ }
+ builders := make([]*UserAttributeDefinitionCreate, rv.Len())
+ for i := 0; i < rv.Len(); i++ {
+ builders[i] = c.Create()
+ setFunc(builders[i], i)
+ }
+ return &UserAttributeDefinitionCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for UserAttributeDefinition.
+func (c *UserAttributeDefinitionClient) Update() *UserAttributeDefinitionUpdate {
+ mutation := newUserAttributeDefinitionMutation(c.config, OpUpdate)
+ return &UserAttributeDefinitionUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *UserAttributeDefinitionClient) UpdateOne(_m *UserAttributeDefinition) *UserAttributeDefinitionUpdateOne {
+ mutation := newUserAttributeDefinitionMutation(c.config, OpUpdateOne, withUserAttributeDefinition(_m))
+ return &UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *UserAttributeDefinitionClient) UpdateOneID(id int64) *UserAttributeDefinitionUpdateOne {
+ mutation := newUserAttributeDefinitionMutation(c.config, OpUpdateOne, withUserAttributeDefinitionID(id))
+ return &UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for UserAttributeDefinition.
+func (c *UserAttributeDefinitionClient) Delete() *UserAttributeDefinitionDelete {
+ mutation := newUserAttributeDefinitionMutation(c.config, OpDelete)
+ return &UserAttributeDefinitionDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *UserAttributeDefinitionClient) DeleteOne(_m *UserAttributeDefinition) *UserAttributeDefinitionDeleteOne {
+ return c.DeleteOneID(_m.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *UserAttributeDefinitionClient) DeleteOneID(id int64) *UserAttributeDefinitionDeleteOne {
+ builder := c.Delete().Where(userattributedefinition.ID(id))
+ builder.mutation.id = &id
+ builder.mutation.op = OpDeleteOne
+ return &UserAttributeDefinitionDeleteOne{builder}
+}
+
+// Query returns a query builder for UserAttributeDefinition.
+func (c *UserAttributeDefinitionClient) Query() *UserAttributeDefinitionQuery {
+ return &UserAttributeDefinitionQuery{
+ config: c.config,
+ ctx: &QueryContext{Type: TypeUserAttributeDefinition},
+ inters: c.Interceptors(),
+ }
+}
+
+// Get returns a UserAttributeDefinition entity by its id.
+func (c *UserAttributeDefinitionClient) Get(ctx context.Context, id int64) (*UserAttributeDefinition, error) {
+ return c.Query().Where(userattributedefinition.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *UserAttributeDefinitionClient) GetX(ctx context.Context, id int64) *UserAttributeDefinition {
+ obj, err := c.Get(ctx, id)
+ if err != nil {
+ panic(err)
+ }
+ return obj
+}
+
+// QueryValues queries the values edge of a UserAttributeDefinition.
+func (c *UserAttributeDefinitionClient) QueryValues(_m *UserAttributeDefinition) *UserAttributeValueQuery {
+ query := (&UserAttributeValueClient{config: c.config}).Query()
+ query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+ id := _m.ID
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributedefinition.Table, userattributedefinition.FieldID, id),
+ sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, userattributedefinition.ValuesTable, userattributedefinition.ValuesColumn),
+ )
+ fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
+ return fromV, nil
+ }
+ return query
+}
+
+// Hooks returns the client hooks.
+func (c *UserAttributeDefinitionClient) Hooks() []Hook {
+ hooks := c.hooks.UserAttributeDefinition
+ return append(hooks[:len(hooks):len(hooks)], userattributedefinition.Hooks[:]...)
+}
+
+// Interceptors returns the client interceptors.
+func (c *UserAttributeDefinitionClient) Interceptors() []Interceptor {
+ inters := c.inters.UserAttributeDefinition
+ return append(inters[:len(inters):len(inters)], userattributedefinition.Interceptors[:]...)
+}
+
+func (c *UserAttributeDefinitionClient) mutate(ctx context.Context, m *UserAttributeDefinitionMutation) (Value, error) {
+ switch m.Op() {
+ case OpCreate:
+ return (&UserAttributeDefinitionCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdate:
+ return (&UserAttributeDefinitionUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdateOne:
+ return (&UserAttributeDefinitionUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpDelete, OpDeleteOne:
+ return (&UserAttributeDefinitionDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+ default:
+ return nil, fmt.Errorf("ent: unknown UserAttributeDefinition mutation op: %q", m.Op())
+ }
+}
+
+// UserAttributeValueClient is a client for the UserAttributeValue schema.
+type UserAttributeValueClient struct {
+ config
+}
+
+// NewUserAttributeValueClient returns a client for the UserAttributeValue from the given config.
+func NewUserAttributeValueClient(c config) *UserAttributeValueClient {
+ return &UserAttributeValueClient{config: c}
+}
+
+// Use adds a list of mutation hooks to the hooks stack.
+// A call to `Use(f, g, h)` equals to `userattributevalue.Hooks(f(g(h())))`.
+func (c *UserAttributeValueClient) Use(hooks ...Hook) {
+ c.hooks.UserAttributeValue = append(c.hooks.UserAttributeValue, hooks...)
+}
+
+// Intercept adds a list of query interceptors to the interceptors stack.
+// A call to `Intercept(f, g, h)` equals to `userattributevalue.Intercept(f(g(h())))`.
+func (c *UserAttributeValueClient) Intercept(interceptors ...Interceptor) {
+ c.inters.UserAttributeValue = append(c.inters.UserAttributeValue, interceptors...)
+}
+
+// Create returns a builder for creating a UserAttributeValue entity.
+func (c *UserAttributeValueClient) Create() *UserAttributeValueCreate {
+ mutation := newUserAttributeValueMutation(c.config, OpCreate)
+ return &UserAttributeValueCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// CreateBulk returns a builder for creating a bulk of UserAttributeValue entities.
+func (c *UserAttributeValueClient) CreateBulk(builders ...*UserAttributeValueCreate) *UserAttributeValueCreateBulk {
+ return &UserAttributeValueCreateBulk{config: c.config, builders: builders}
+}
+
+// MapCreateBulk creates a bulk creation builder from the given slice. For each item in the slice, the function creates
+// a builder and applies setFunc on it.
+func (c *UserAttributeValueClient) MapCreateBulk(slice any, setFunc func(*UserAttributeValueCreate, int)) *UserAttributeValueCreateBulk {
+ rv := reflect.ValueOf(slice)
+ if rv.Kind() != reflect.Slice {
+ return &UserAttributeValueCreateBulk{err: fmt.Errorf("calling to UserAttributeValueClient.MapCreateBulk with wrong type %T, need slice", slice)}
+ }
+ builders := make([]*UserAttributeValueCreate, rv.Len())
+ for i := 0; i < rv.Len(); i++ {
+ builders[i] = c.Create()
+ setFunc(builders[i], i)
+ }
+ return &UserAttributeValueCreateBulk{config: c.config, builders: builders}
+}
+
+// Update returns an update builder for UserAttributeValue.
+func (c *UserAttributeValueClient) Update() *UserAttributeValueUpdate {
+ mutation := newUserAttributeValueMutation(c.config, OpUpdate)
+ return &UserAttributeValueUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOne returns an update builder for the given entity.
+func (c *UserAttributeValueClient) UpdateOne(_m *UserAttributeValue) *UserAttributeValueUpdateOne {
+ mutation := newUserAttributeValueMutation(c.config, OpUpdateOne, withUserAttributeValue(_m))
+ return &UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// UpdateOneID returns an update builder for the given id.
+func (c *UserAttributeValueClient) UpdateOneID(id int64) *UserAttributeValueUpdateOne {
+ mutation := newUserAttributeValueMutation(c.config, OpUpdateOne, withUserAttributeValueID(id))
+ return &UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// Delete returns a delete builder for UserAttributeValue.
+func (c *UserAttributeValueClient) Delete() *UserAttributeValueDelete {
+ mutation := newUserAttributeValueMutation(c.config, OpDelete)
+ return &UserAttributeValueDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
+}
+
+// DeleteOne returns a builder for deleting the given entity.
+func (c *UserAttributeValueClient) DeleteOne(_m *UserAttributeValue) *UserAttributeValueDeleteOne {
+ return c.DeleteOneID(_m.ID)
+}
+
+// DeleteOneID returns a builder for deleting the given entity by its id.
+func (c *UserAttributeValueClient) DeleteOneID(id int64) *UserAttributeValueDeleteOne {
+ builder := c.Delete().Where(userattributevalue.ID(id))
+ builder.mutation.id = &id
+ builder.mutation.op = OpDeleteOne
+ return &UserAttributeValueDeleteOne{builder}
+}
+
+// Query returns a query builder for UserAttributeValue.
+func (c *UserAttributeValueClient) Query() *UserAttributeValueQuery {
+ return &UserAttributeValueQuery{
+ config: c.config,
+ ctx: &QueryContext{Type: TypeUserAttributeValue},
+ inters: c.Interceptors(),
+ }
+}
+
+// Get returns a UserAttributeValue entity by its id.
+func (c *UserAttributeValueClient) Get(ctx context.Context, id int64) (*UserAttributeValue, error) {
+ return c.Query().Where(userattributevalue.ID(id)).Only(ctx)
+}
+
+// GetX is like Get, but panics if an error occurs.
+func (c *UserAttributeValueClient) GetX(ctx context.Context, id int64) *UserAttributeValue {
+ obj, err := c.Get(ctx, id)
+ if err != nil {
+ panic(err)
+ }
+ return obj
+}
+
+// QueryUser queries the user edge of a UserAttributeValue.
+func (c *UserAttributeValueClient) QueryUser(_m *UserAttributeValue) *UserQuery {
+ query := (&UserClient{config: c.config}).Query()
+ query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+ id := _m.ID
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, id),
+ sqlgraph.To(user.Table, user.FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.UserTable, userattributevalue.UserColumn),
+ )
+ fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
+ return fromV, nil
+ }
+ return query
+}
+
+// QueryDefinition queries the definition edge of a UserAttributeValue.
+func (c *UserAttributeValueClient) QueryDefinition(_m *UserAttributeValue) *UserAttributeDefinitionQuery {
+ query := (&UserAttributeDefinitionClient{config: c.config}).Query()
+ query.path = func(context.Context) (fromV *sql.Selector, _ error) {
+ id := _m.ID
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, id),
+ sqlgraph.To(userattributedefinition.Table, userattributedefinition.FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.DefinitionTable, userattributevalue.DefinitionColumn),
+ )
+ fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
+ return fromV, nil
+ }
+ return query
+}
+
+// Hooks returns the client hooks.
+func (c *UserAttributeValueClient) Hooks() []Hook {
+ return c.hooks.UserAttributeValue
+}
+
+// Interceptors returns the client interceptors.
+func (c *UserAttributeValueClient) Interceptors() []Interceptor {
+ return c.inters.UserAttributeValue
+}
+
+func (c *UserAttributeValueClient) mutate(ctx context.Context, m *UserAttributeValueMutation) (Value, error) {
+ switch m.Op() {
+ case OpCreate:
+ return (&UserAttributeValueCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdate:
+ return (&UserAttributeValueUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpUpdateOne:
+ return (&UserAttributeValueUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
+ case OpDelete, OpDeleteOne:
+ return (&UserAttributeValueDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
+ default:
+ return nil, fmt.Errorf("ent: unknown UserAttributeValue mutation op: %q", m.Op())
+ }
+}
+
// UserSubscriptionClient is a client for the UserSubscription schema.
type UserSubscriptionClient struct {
config
@@ -2278,11 +2628,13 @@ func (c *UserSubscriptionClient) mutate(ctx context.Context, m *UserSubscription
type (
hooks struct {
Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog,
- User, UserAllowedGroup, UserSubscription []ent.Hook
+ User, UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
+ UserSubscription []ent.Hook
}
inters struct {
Account, AccountGroup, ApiKey, Group, Proxy, RedeemCode, Setting, UsageLog,
- User, UserAllowedGroup, UserSubscription []ent.Interceptor
+ User, UserAllowedGroup, UserAttributeDefinition, UserAttributeValue,
+ UserSubscription []ent.Interceptor
}
)
diff --git a/backend/ent/ent.go b/backend/ent/ent.go
index 29890206..49437ad7 100644
--- a/backend/ent/ent.go
+++ b/backend/ent/ent.go
@@ -22,6 +22,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -83,17 +85,19 @@ var (
func checkColumn(t, c string) error {
initCheck.Do(func() {
columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
- account.Table: account.ValidColumn,
- accountgroup.Table: accountgroup.ValidColumn,
- apikey.Table: apikey.ValidColumn,
- group.Table: group.ValidColumn,
- proxy.Table: proxy.ValidColumn,
- redeemcode.Table: redeemcode.ValidColumn,
- setting.Table: setting.ValidColumn,
- usagelog.Table: usagelog.ValidColumn,
- user.Table: user.ValidColumn,
- userallowedgroup.Table: userallowedgroup.ValidColumn,
- usersubscription.Table: usersubscription.ValidColumn,
+ account.Table: account.ValidColumn,
+ accountgroup.Table: accountgroup.ValidColumn,
+ apikey.Table: apikey.ValidColumn,
+ group.Table: group.ValidColumn,
+ proxy.Table: proxy.ValidColumn,
+ redeemcode.Table: redeemcode.ValidColumn,
+ setting.Table: setting.ValidColumn,
+ usagelog.Table: usagelog.ValidColumn,
+ user.Table: user.ValidColumn,
+ userallowedgroup.Table: userallowedgroup.ValidColumn,
+ userattributedefinition.Table: userattributedefinition.ValidColumn,
+ userattributevalue.Table: userattributevalue.ValidColumn,
+ usersubscription.Table: usersubscription.ValidColumn,
})
})
return columnCheck(t, c)
diff --git a/backend/ent/hook/hook.go b/backend/ent/hook/hook.go
index 33955cbb..3aa5d186 100644
--- a/backend/ent/hook/hook.go
+++ b/backend/ent/hook/hook.go
@@ -129,6 +129,30 @@ func (f UserAllowedGroupFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.V
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAllowedGroupMutation", m)
}
+// The UserAttributeDefinitionFunc type is an adapter to allow the use of ordinary
+// function as UserAttributeDefinition mutator.
+type UserAttributeDefinitionFunc func(context.Context, *ent.UserAttributeDefinitionMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f UserAttributeDefinitionFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+ if mv, ok := m.(*ent.UserAttributeDefinitionMutation); ok {
+ return f(ctx, mv)
+ }
+ return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAttributeDefinitionMutation", m)
+}
+
+// The UserAttributeValueFunc type is an adapter to allow the use of ordinary
+// function as UserAttributeValue mutator.
+type UserAttributeValueFunc func(context.Context, *ent.UserAttributeValueMutation) (ent.Value, error)
+
+// Mutate calls f(ctx, m).
+func (f UserAttributeValueFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
+ if mv, ok := m.(*ent.UserAttributeValueMutation); ok {
+ return f(ctx, mv)
+ }
+ return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAttributeValueMutation", m)
+}
+
// The UserSubscriptionFunc type is an adapter to allow the use of ordinary
// function as UserSubscription mutator.
type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionMutation) (ent.Value, error)
diff --git a/backend/ent/intercept/intercept.go b/backend/ent/intercept/intercept.go
index 9815f477..9f694d67 100644
--- a/backend/ent/intercept/intercept.go
+++ b/backend/ent/intercept/intercept.go
@@ -19,6 +19,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -348,6 +350,60 @@ func (f TraverseUserAllowedGroup) Traverse(ctx context.Context, q ent.Query) err
return fmt.Errorf("unexpected query type %T. expect *ent.UserAllowedGroupQuery", q)
}
+// The UserAttributeDefinitionFunc type is an adapter to allow the use of ordinary function as a Querier.
+type UserAttributeDefinitionFunc func(context.Context, *ent.UserAttributeDefinitionQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f UserAttributeDefinitionFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+ if q, ok := q.(*ent.UserAttributeDefinitionQuery); ok {
+ return f(ctx, q)
+ }
+ return nil, fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeDefinitionQuery", q)
+}
+
+// The TraverseUserAttributeDefinition type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseUserAttributeDefinition func(context.Context, *ent.UserAttributeDefinitionQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseUserAttributeDefinition) Intercept(next ent.Querier) ent.Querier {
+ return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseUserAttributeDefinition) Traverse(ctx context.Context, q ent.Query) error {
+ if q, ok := q.(*ent.UserAttributeDefinitionQuery); ok {
+ return f(ctx, q)
+ }
+ return fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeDefinitionQuery", q)
+}
+
+// The UserAttributeValueFunc type is an adapter to allow the use of ordinary function as a Querier.
+type UserAttributeValueFunc func(context.Context, *ent.UserAttributeValueQuery) (ent.Value, error)
+
+// Query calls f(ctx, q).
+func (f UserAttributeValueFunc) Query(ctx context.Context, q ent.Query) (ent.Value, error) {
+ if q, ok := q.(*ent.UserAttributeValueQuery); ok {
+ return f(ctx, q)
+ }
+ return nil, fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeValueQuery", q)
+}
+
+// The TraverseUserAttributeValue type is an adapter to allow the use of ordinary function as Traverser.
+type TraverseUserAttributeValue func(context.Context, *ent.UserAttributeValueQuery) error
+
+// Intercept is a dummy implementation of Intercept that returns the next Querier in the pipeline.
+func (f TraverseUserAttributeValue) Intercept(next ent.Querier) ent.Querier {
+ return next
+}
+
+// Traverse calls f(ctx, q).
+func (f TraverseUserAttributeValue) Traverse(ctx context.Context, q ent.Query) error {
+ if q, ok := q.(*ent.UserAttributeValueQuery); ok {
+ return f(ctx, q)
+ }
+ return fmt.Errorf("unexpected query type %T. expect *ent.UserAttributeValueQuery", q)
+}
+
// The UserSubscriptionFunc type is an adapter to allow the use of ordinary function as a Querier.
type UserSubscriptionFunc func(context.Context, *ent.UserSubscriptionQuery) (ent.Value, error)
@@ -398,6 +454,10 @@ func NewQuery(q ent.Query) (Query, error) {
return &query[*ent.UserQuery, predicate.User, user.OrderOption]{typ: ent.TypeUser, tq: q}, nil
case *ent.UserAllowedGroupQuery:
return &query[*ent.UserAllowedGroupQuery, predicate.UserAllowedGroup, userallowedgroup.OrderOption]{typ: ent.TypeUserAllowedGroup, tq: q}, nil
+ case *ent.UserAttributeDefinitionQuery:
+ return &query[*ent.UserAttributeDefinitionQuery, predicate.UserAttributeDefinition, userattributedefinition.OrderOption]{typ: ent.TypeUserAttributeDefinition, tq: q}, nil
+ case *ent.UserAttributeValueQuery:
+ return &query[*ent.UserAttributeValueQuery, predicate.UserAttributeValue, userattributevalue.OrderOption]{typ: ent.TypeUserAttributeValue, tq: q}, nil
case *ent.UserSubscriptionQuery:
return &query[*ent.UserSubscriptionQuery, predicate.UserSubscription, usersubscription.OrderOption]{typ: ent.TypeUserSubscription, tq: q}, nil
default:
diff --git a/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go
index c9a1675e..d532b34b 100644
--- a/backend/ent/migrate/schema.go
+++ b/backend/ent/migrate/schema.go
@@ -477,7 +477,6 @@ var (
{Name: "concurrency", Type: field.TypeInt, Default: 5},
{Name: "status", Type: field.TypeString, Size: 20, Default: "active"},
{Name: "username", Type: field.TypeString, Size: 100, Default: ""},
- {Name: "wechat", Type: field.TypeString, Size: 100, Default: ""},
{Name: "notes", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
}
// UsersTable holds the schema information for the "users" table.
@@ -531,6 +530,92 @@ var (
},
},
}
+ // UserAttributeDefinitionsColumns holds the columns for the "user_attribute_definitions" table.
+ UserAttributeDefinitionsColumns = []*schema.Column{
+ {Name: "id", Type: field.TypeInt64, Increment: true},
+ {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "deleted_at", Type: field.TypeTime, Nullable: true, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "key", Type: field.TypeString, Size: 100},
+ {Name: "name", Type: field.TypeString, Size: 255},
+ {Name: "description", Type: field.TypeString, Default: "", SchemaType: map[string]string{"postgres": "text"}},
+ {Name: "type", Type: field.TypeString, Size: 20},
+ {Name: "options", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
+ {Name: "required", Type: field.TypeBool, Default: false},
+ {Name: "validation", Type: field.TypeJSON, SchemaType: map[string]string{"postgres": "jsonb"}},
+ {Name: "placeholder", Type: field.TypeString, Size: 255, Default: ""},
+ {Name: "display_order", Type: field.TypeInt, Default: 0},
+ {Name: "enabled", Type: field.TypeBool, Default: true},
+ }
+ // UserAttributeDefinitionsTable holds the schema information for the "user_attribute_definitions" table.
+ UserAttributeDefinitionsTable = &schema.Table{
+ Name: "user_attribute_definitions",
+ Columns: UserAttributeDefinitionsColumns,
+ PrimaryKey: []*schema.Column{UserAttributeDefinitionsColumns[0]},
+ Indexes: []*schema.Index{
+ {
+ Name: "userattributedefinition_key",
+ Unique: false,
+ Columns: []*schema.Column{UserAttributeDefinitionsColumns[4]},
+ },
+ {
+ Name: "userattributedefinition_enabled",
+ Unique: false,
+ Columns: []*schema.Column{UserAttributeDefinitionsColumns[13]},
+ },
+ {
+ Name: "userattributedefinition_display_order",
+ Unique: false,
+ Columns: []*schema.Column{UserAttributeDefinitionsColumns[12]},
+ },
+ {
+ Name: "userattributedefinition_deleted_at",
+ Unique: false,
+ Columns: []*schema.Column{UserAttributeDefinitionsColumns[3]},
+ },
+ },
+ }
+ // UserAttributeValuesColumns holds the columns for the "user_attribute_values" table.
+ UserAttributeValuesColumns = []*schema.Column{
+ {Name: "id", Type: field.TypeInt64, Increment: true},
+ {Name: "created_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "updated_at", Type: field.TypeTime, SchemaType: map[string]string{"postgres": "timestamptz"}},
+ {Name: "value", Type: field.TypeString, Size: 2147483647, Default: ""},
+ {Name: "user_id", Type: field.TypeInt64},
+ {Name: "attribute_id", Type: field.TypeInt64},
+ }
+ // UserAttributeValuesTable holds the schema information for the "user_attribute_values" table.
+ UserAttributeValuesTable = &schema.Table{
+ Name: "user_attribute_values",
+ Columns: UserAttributeValuesColumns,
+ PrimaryKey: []*schema.Column{UserAttributeValuesColumns[0]},
+ ForeignKeys: []*schema.ForeignKey{
+ {
+ Symbol: "user_attribute_values_users_attribute_values",
+ Columns: []*schema.Column{UserAttributeValuesColumns[4]},
+ RefColumns: []*schema.Column{UsersColumns[0]},
+ OnDelete: schema.NoAction,
+ },
+ {
+ Symbol: "user_attribute_values_user_attribute_definitions_values",
+ Columns: []*schema.Column{UserAttributeValuesColumns[5]},
+ RefColumns: []*schema.Column{UserAttributeDefinitionsColumns[0]},
+ OnDelete: schema.NoAction,
+ },
+ },
+ Indexes: []*schema.Index{
+ {
+ Name: "userattributevalue_user_id_attribute_id",
+ Unique: true,
+ Columns: []*schema.Column{UserAttributeValuesColumns[4], UserAttributeValuesColumns[5]},
+ },
+ {
+ Name: "userattributevalue_attribute_id",
+ Unique: false,
+ Columns: []*schema.Column{UserAttributeValuesColumns[5]},
+ },
+ },
+ }
// UserSubscriptionsColumns holds the columns for the "user_subscriptions" table.
UserSubscriptionsColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt64, Increment: true},
@@ -627,6 +712,8 @@ var (
UsageLogsTable,
UsersTable,
UserAllowedGroupsTable,
+ UserAttributeDefinitionsTable,
+ UserAttributeValuesTable,
UserSubscriptionsTable,
}
)
@@ -676,6 +763,14 @@ func init() {
UserAllowedGroupsTable.Annotation = &entsql.Annotation{
Table: "user_allowed_groups",
}
+ UserAttributeDefinitionsTable.Annotation = &entsql.Annotation{
+ Table: "user_attribute_definitions",
+ }
+ UserAttributeValuesTable.ForeignKeys[0].RefTable = UsersTable
+ UserAttributeValuesTable.ForeignKeys[1].RefTable = UserAttributeDefinitionsTable
+ UserAttributeValuesTable.Annotation = &entsql.Annotation{
+ Table: "user_attribute_values",
+ }
UserSubscriptionsTable.ForeignKeys[0].RefTable = GroupsTable
UserSubscriptionsTable.ForeignKeys[1].RefTable = UsersTable
UserSubscriptionsTable.ForeignKeys[2].RefTable = UsersTable
diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go
index 9e4359ab..7d5fd2ad 100644
--- a/backend/ent/mutation.go
+++ b/backend/ent/mutation.go
@@ -22,6 +22,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -34,17 +36,19 @@ const (
OpUpdateOne = ent.OpUpdateOne
// Node types.
- TypeAccount = "Account"
- TypeAccountGroup = "AccountGroup"
- TypeApiKey = "ApiKey"
- TypeGroup = "Group"
- TypeProxy = "Proxy"
- TypeRedeemCode = "RedeemCode"
- TypeSetting = "Setting"
- TypeUsageLog = "UsageLog"
- TypeUser = "User"
- TypeUserAllowedGroup = "UserAllowedGroup"
- TypeUserSubscription = "UserSubscription"
+ TypeAccount = "Account"
+ TypeAccountGroup = "AccountGroup"
+ TypeApiKey = "ApiKey"
+ TypeGroup = "Group"
+ TypeProxy = "Proxy"
+ TypeRedeemCode = "RedeemCode"
+ TypeSetting = "Setting"
+ TypeUsageLog = "UsageLog"
+ TypeUser = "User"
+ TypeUserAllowedGroup = "UserAllowedGroup"
+ TypeUserAttributeDefinition = "UserAttributeDefinition"
+ TypeUserAttributeValue = "UserAttributeValue"
+ TypeUserSubscription = "UserSubscription"
)
// AccountMutation represents an operation that mutates the Account nodes in the graph.
@@ -10158,7 +10162,6 @@ type UserMutation struct {
addconcurrency *int
status *string
username *string
- wechat *string
notes *string
clearedFields map[string]struct{}
api_keys map[int64]struct{}
@@ -10179,6 +10182,9 @@ type UserMutation struct {
usage_logs map[int64]struct{}
removedusage_logs map[int64]struct{}
clearedusage_logs bool
+ attribute_values map[int64]struct{}
+ removedattribute_values map[int64]struct{}
+ clearedattribute_values bool
done bool
oldValue func(context.Context) (*User, error)
predicates []predicate.User
@@ -10695,42 +10701,6 @@ func (m *UserMutation) ResetUsername() {
m.username = nil
}
-// SetWechat sets the "wechat" field.
-func (m *UserMutation) SetWechat(s string) {
- m.wechat = &s
-}
-
-// Wechat returns the value of the "wechat" field in the mutation.
-func (m *UserMutation) Wechat() (r string, exists bool) {
- v := m.wechat
- if v == nil {
- return
- }
- return *v, true
-}
-
-// OldWechat returns the old "wechat" field's value of the User entity.
-// If the User object wasn't provided to the builder, the object is fetched from the database.
-// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
-func (m *UserMutation) OldWechat(ctx context.Context) (v string, err error) {
- if !m.op.Is(OpUpdateOne) {
- return v, errors.New("OldWechat is only allowed on UpdateOne operations")
- }
- if m.id == nil || m.oldValue == nil {
- return v, errors.New("OldWechat requires an ID field in the mutation")
- }
- oldValue, err := m.oldValue(ctx)
- if err != nil {
- return v, fmt.Errorf("querying old value for OldWechat: %w", err)
- }
- return oldValue.Wechat, nil
-}
-
-// ResetWechat resets all changes to the "wechat" field.
-func (m *UserMutation) ResetWechat() {
- m.wechat = nil
-}
-
// SetNotes sets the "notes" field.
func (m *UserMutation) SetNotes(s string) {
m.notes = &s
@@ -11091,6 +11061,60 @@ func (m *UserMutation) ResetUsageLogs() {
m.removedusage_logs = nil
}
+// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by ids.
+func (m *UserMutation) AddAttributeValueIDs(ids ...int64) {
+ if m.attribute_values == nil {
+ m.attribute_values = make(map[int64]struct{})
+ }
+ for i := range ids {
+ m.attribute_values[ids[i]] = struct{}{}
+ }
+}
+
+// ClearAttributeValues clears the "attribute_values" edge to the UserAttributeValue entity.
+func (m *UserMutation) ClearAttributeValues() {
+ m.clearedattribute_values = true
+}
+
+// AttributeValuesCleared reports if the "attribute_values" edge to the UserAttributeValue entity was cleared.
+func (m *UserMutation) AttributeValuesCleared() bool {
+ return m.clearedattribute_values
+}
+
+// RemoveAttributeValueIDs removes the "attribute_values" edge to the UserAttributeValue entity by IDs.
+func (m *UserMutation) RemoveAttributeValueIDs(ids ...int64) {
+ if m.removedattribute_values == nil {
+ m.removedattribute_values = make(map[int64]struct{})
+ }
+ for i := range ids {
+ delete(m.attribute_values, ids[i])
+ m.removedattribute_values[ids[i]] = struct{}{}
+ }
+}
+
+// RemovedAttributeValues returns the removed IDs of the "attribute_values" edge to the UserAttributeValue entity.
+func (m *UserMutation) RemovedAttributeValuesIDs() (ids []int64) {
+ for id := range m.removedattribute_values {
+ ids = append(ids, id)
+ }
+ return
+}
+
+// AttributeValuesIDs returns the "attribute_values" edge IDs in the mutation.
+func (m *UserMutation) AttributeValuesIDs() (ids []int64) {
+ for id := range m.attribute_values {
+ ids = append(ids, id)
+ }
+ return
+}
+
+// ResetAttributeValues resets all changes to the "attribute_values" edge.
+func (m *UserMutation) ResetAttributeValues() {
+ m.attribute_values = nil
+ m.clearedattribute_values = false
+ m.removedattribute_values = nil
+}
+
// Where appends a list predicates to the UserMutation builder.
func (m *UserMutation) Where(ps ...predicate.User) {
m.predicates = append(m.predicates, ps...)
@@ -11125,7 +11149,7 @@ func (m *UserMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *UserMutation) Fields() []string {
- fields := make([]string, 0, 12)
+ fields := make([]string, 0, 11)
if m.created_at != nil {
fields = append(fields, user.FieldCreatedAt)
}
@@ -11156,9 +11180,6 @@ func (m *UserMutation) Fields() []string {
if m.username != nil {
fields = append(fields, user.FieldUsername)
}
- if m.wechat != nil {
- fields = append(fields, user.FieldWechat)
- }
if m.notes != nil {
fields = append(fields, user.FieldNotes)
}
@@ -11190,8 +11211,6 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) {
return m.Status()
case user.FieldUsername:
return m.Username()
- case user.FieldWechat:
- return m.Wechat()
case user.FieldNotes:
return m.Notes()
}
@@ -11223,8 +11242,6 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er
return m.OldStatus(ctx)
case user.FieldUsername:
return m.OldUsername(ctx)
- case user.FieldWechat:
- return m.OldWechat(ctx)
case user.FieldNotes:
return m.OldNotes(ctx)
}
@@ -11306,13 +11323,6 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
}
m.SetUsername(v)
return nil
- case user.FieldWechat:
- v, ok := value.(string)
- if !ok {
- return fmt.Errorf("unexpected type %T for field %s", value, name)
- }
- m.SetWechat(v)
- return nil
case user.FieldNotes:
v, ok := value.(string)
if !ok {
@@ -11435,9 +11445,6 @@ func (m *UserMutation) ResetField(name string) error {
case user.FieldUsername:
m.ResetUsername()
return nil
- case user.FieldWechat:
- m.ResetWechat()
- return nil
case user.FieldNotes:
m.ResetNotes()
return nil
@@ -11447,7 +11454,7 @@ func (m *UserMutation) ResetField(name string) error {
// AddedEdges returns all edge names that were set/added in this mutation.
func (m *UserMutation) AddedEdges() []string {
- edges := make([]string, 0, 6)
+ edges := make([]string, 0, 7)
if m.api_keys != nil {
edges = append(edges, user.EdgeAPIKeys)
}
@@ -11466,6 +11473,9 @@ func (m *UserMutation) AddedEdges() []string {
if m.usage_logs != nil {
edges = append(edges, user.EdgeUsageLogs)
}
+ if m.attribute_values != nil {
+ edges = append(edges, user.EdgeAttributeValues)
+ }
return edges
}
@@ -11509,13 +11519,19 @@ func (m *UserMutation) AddedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
+ case user.EdgeAttributeValues:
+ ids := make([]ent.Value, 0, len(m.attribute_values))
+ for id := range m.attribute_values {
+ ids = append(ids, id)
+ }
+ return ids
}
return nil
}
// RemovedEdges returns all edge names that were removed in this mutation.
func (m *UserMutation) RemovedEdges() []string {
- edges := make([]string, 0, 6)
+ edges := make([]string, 0, 7)
if m.removedapi_keys != nil {
edges = append(edges, user.EdgeAPIKeys)
}
@@ -11534,6 +11550,9 @@ func (m *UserMutation) RemovedEdges() []string {
if m.removedusage_logs != nil {
edges = append(edges, user.EdgeUsageLogs)
}
+ if m.removedattribute_values != nil {
+ edges = append(edges, user.EdgeAttributeValues)
+ }
return edges
}
@@ -11577,13 +11596,19 @@ func (m *UserMutation) RemovedIDs(name string) []ent.Value {
ids = append(ids, id)
}
return ids
+ case user.EdgeAttributeValues:
+ ids := make([]ent.Value, 0, len(m.removedattribute_values))
+ for id := range m.removedattribute_values {
+ ids = append(ids, id)
+ }
+ return ids
}
return nil
}
// ClearedEdges returns all edge names that were cleared in this mutation.
func (m *UserMutation) ClearedEdges() []string {
- edges := make([]string, 0, 6)
+ edges := make([]string, 0, 7)
if m.clearedapi_keys {
edges = append(edges, user.EdgeAPIKeys)
}
@@ -11602,6 +11627,9 @@ func (m *UserMutation) ClearedEdges() []string {
if m.clearedusage_logs {
edges = append(edges, user.EdgeUsageLogs)
}
+ if m.clearedattribute_values {
+ edges = append(edges, user.EdgeAttributeValues)
+ }
return edges
}
@@ -11621,6 +11649,8 @@ func (m *UserMutation) EdgeCleared(name string) bool {
return m.clearedallowed_groups
case user.EdgeUsageLogs:
return m.clearedusage_logs
+ case user.EdgeAttributeValues:
+ return m.clearedattribute_values
}
return false
}
@@ -11655,6 +11685,9 @@ func (m *UserMutation) ResetEdge(name string) error {
case user.EdgeUsageLogs:
m.ResetUsageLogs()
return nil
+ case user.EdgeAttributeValues:
+ m.ResetAttributeValues()
+ return nil
}
return fmt.Errorf("unknown User edge %s", name)
}
@@ -12076,6 +12109,1805 @@ func (m *UserAllowedGroupMutation) ResetEdge(name string) error {
return fmt.Errorf("unknown UserAllowedGroup edge %s", name)
}
+// UserAttributeDefinitionMutation represents an operation that mutates the UserAttributeDefinition nodes in the graph.
+type UserAttributeDefinitionMutation struct {
+ config
+ op Op
+ typ string
+ id *int64
+ created_at *time.Time
+ updated_at *time.Time
+ deleted_at *time.Time
+ key *string
+ name *string
+ description *string
+ _type *string
+ options *[]map[string]interface{}
+ appendoptions []map[string]interface{}
+ required *bool
+ validation *map[string]interface{}
+ placeholder *string
+ display_order *int
+ adddisplay_order *int
+ enabled *bool
+ clearedFields map[string]struct{}
+ values map[int64]struct{}
+ removedvalues map[int64]struct{}
+ clearedvalues bool
+ done bool
+ oldValue func(context.Context) (*UserAttributeDefinition, error)
+ predicates []predicate.UserAttributeDefinition
+}
+
+var _ ent.Mutation = (*UserAttributeDefinitionMutation)(nil)
+
+// userattributedefinitionOption allows management of the mutation configuration using functional options.
+type userattributedefinitionOption func(*UserAttributeDefinitionMutation)
+
+// newUserAttributeDefinitionMutation creates new mutation for the UserAttributeDefinition entity.
+func newUserAttributeDefinitionMutation(c config, op Op, opts ...userattributedefinitionOption) *UserAttributeDefinitionMutation {
+ m := &UserAttributeDefinitionMutation{
+ config: c,
+ op: op,
+ typ: TypeUserAttributeDefinition,
+ clearedFields: make(map[string]struct{}),
+ }
+ for _, opt := range opts {
+ opt(m)
+ }
+ return m
+}
+
+// withUserAttributeDefinitionID sets the ID field of the mutation.
+func withUserAttributeDefinitionID(id int64) userattributedefinitionOption {
+ return func(m *UserAttributeDefinitionMutation) {
+ var (
+ err error
+ once sync.Once
+ value *UserAttributeDefinition
+ )
+ m.oldValue = func(ctx context.Context) (*UserAttributeDefinition, error) {
+ once.Do(func() {
+ if m.done {
+ err = errors.New("querying old values post mutation is not allowed")
+ } else {
+ value, err = m.Client().UserAttributeDefinition.Get(ctx, id)
+ }
+ })
+ return value, err
+ }
+ m.id = &id
+ }
+}
+
+// withUserAttributeDefinition sets the old UserAttributeDefinition of the mutation.
+func withUserAttributeDefinition(node *UserAttributeDefinition) userattributedefinitionOption {
+ return func(m *UserAttributeDefinitionMutation) {
+ m.oldValue = func(context.Context) (*UserAttributeDefinition, error) {
+ return node, nil
+ }
+ m.id = &node.ID
+ }
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m UserAttributeDefinitionMutation) Client() *Client {
+ client := &Client{config: m.config}
+ client.init()
+ return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m UserAttributeDefinitionMutation) Tx() (*Tx, error) {
+ if _, ok := m.driver.(*txDriver); !ok {
+ return nil, errors.New("ent: mutation is not running in a transaction")
+ }
+ tx := &Tx{config: m.config}
+ tx.init()
+ return tx, nil
+}
+
+// ID returns the ID value in the mutation. Note that the ID is only available
+// if it was provided to the builder or after it was returned from the database.
+func (m *UserAttributeDefinitionMutation) ID() (id int64, exists bool) {
+ if m.id == nil {
+ return
+ }
+ return *m.id, true
+}
+
+// IDs queries the database and returns the entity ids that match the mutation's predicate.
+// That means, if the mutation is applied within a transaction with an isolation level such
+// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
+// or updated by the mutation.
+func (m *UserAttributeDefinitionMutation) IDs(ctx context.Context) ([]int64, error) {
+ switch {
+ case m.op.Is(OpUpdateOne | OpDeleteOne):
+ id, exists := m.ID()
+ if exists {
+ return []int64{id}, nil
+ }
+ fallthrough
+ case m.op.Is(OpUpdate | OpDelete):
+ return m.Client().UserAttributeDefinition.Query().Where(m.predicates...).IDs(ctx)
+ default:
+ return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
+ }
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (m *UserAttributeDefinitionMutation) SetCreatedAt(t time.Time) {
+ m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *UserAttributeDefinitionMutation) CreatedAt() (r time.Time, exists bool) {
+ v := m.created_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldCreatedAt returns the old "created_at" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldCreatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
+ }
+ return oldValue.CreatedAt, nil
+}
+
+// ResetCreatedAt resets all changes to the "created_at" field.
+func (m *UserAttributeDefinitionMutation) ResetCreatedAt() {
+ m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *UserAttributeDefinitionMutation) SetUpdatedAt(t time.Time) {
+ m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *UserAttributeDefinitionMutation) UpdatedAt() (r time.Time, exists bool) {
+ v := m.updated_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUpdatedAt returns the old "updated_at" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
+ }
+ return oldValue.UpdatedAt, nil
+}
+
+// ResetUpdatedAt resets all changes to the "updated_at" field.
+func (m *UserAttributeDefinitionMutation) ResetUpdatedAt() {
+ m.updated_at = nil
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (m *UserAttributeDefinitionMutation) SetDeletedAt(t time.Time) {
+ m.deleted_at = &t
+}
+
+// DeletedAt returns the value of the "deleted_at" field in the mutation.
+func (m *UserAttributeDefinitionMutation) DeletedAt() (r time.Time, exists bool) {
+ v := m.deleted_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldDeletedAt returns the old "deleted_at" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldDeletedAt(ctx context.Context) (v *time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldDeletedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldDeletedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldDeletedAt: %w", err)
+ }
+ return oldValue.DeletedAt, nil
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (m *UserAttributeDefinitionMutation) ClearDeletedAt() {
+ m.deleted_at = nil
+ m.clearedFields[userattributedefinition.FieldDeletedAt] = struct{}{}
+}
+
+// DeletedAtCleared returns if the "deleted_at" field was cleared in this mutation.
+func (m *UserAttributeDefinitionMutation) DeletedAtCleared() bool {
+ _, ok := m.clearedFields[userattributedefinition.FieldDeletedAt]
+ return ok
+}
+
+// ResetDeletedAt resets all changes to the "deleted_at" field.
+func (m *UserAttributeDefinitionMutation) ResetDeletedAt() {
+ m.deleted_at = nil
+ delete(m.clearedFields, userattributedefinition.FieldDeletedAt)
+}
+
+// SetKey sets the "key" field.
+func (m *UserAttributeDefinitionMutation) SetKey(s string) {
+ m.key = &s
+}
+
+// Key returns the value of the "key" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Key() (r string, exists bool) {
+ v := m.key
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldKey returns the old "key" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldKey(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldKey is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldKey requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldKey: %w", err)
+ }
+ return oldValue.Key, nil
+}
+
+// ResetKey resets all changes to the "key" field.
+func (m *UserAttributeDefinitionMutation) ResetKey() {
+ m.key = nil
+}
+
+// SetName sets the "name" field.
+func (m *UserAttributeDefinitionMutation) SetName(s string) {
+ m.name = &s
+}
+
+// Name returns the value of the "name" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Name() (r string, exists bool) {
+ v := m.name
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldName returns the old "name" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldName(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldName is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldName requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldName: %w", err)
+ }
+ return oldValue.Name, nil
+}
+
+// ResetName resets all changes to the "name" field.
+func (m *UserAttributeDefinitionMutation) ResetName() {
+ m.name = nil
+}
+
+// SetDescription sets the "description" field.
+func (m *UserAttributeDefinitionMutation) SetDescription(s string) {
+ m.description = &s
+}
+
+// Description returns the value of the "description" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Description() (r string, exists bool) {
+ v := m.description
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldDescription returns the old "description" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldDescription(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldDescription is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldDescription requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldDescription: %w", err)
+ }
+ return oldValue.Description, nil
+}
+
+// ResetDescription resets all changes to the "description" field.
+func (m *UserAttributeDefinitionMutation) ResetDescription() {
+ m.description = nil
+}
+
+// SetType sets the "type" field.
+func (m *UserAttributeDefinitionMutation) SetType(s string) {
+ m._type = &s
+}
+
+// GetType returns the value of the "type" field in the mutation.
+func (m *UserAttributeDefinitionMutation) GetType() (r string, exists bool) {
+ v := m._type
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldType returns the old "type" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldType(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldType is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldType requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldType: %w", err)
+ }
+ return oldValue.Type, nil
+}
+
+// ResetType resets all changes to the "type" field.
+func (m *UserAttributeDefinitionMutation) ResetType() {
+ m._type = nil
+}
+
+// SetOptions sets the "options" field.
+func (m *UserAttributeDefinitionMutation) SetOptions(value []map[string]interface{}) {
+ m.options = &value
+ m.appendoptions = nil
+}
+
+// Options returns the value of the "options" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Options() (r []map[string]interface{}, exists bool) {
+ v := m.options
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldOptions returns the old "options" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldOptions(ctx context.Context) (v []map[string]interface{}, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldOptions is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldOptions requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldOptions: %w", err)
+ }
+ return oldValue.Options, nil
+}
+
+// AppendOptions adds value to the "options" field.
+func (m *UserAttributeDefinitionMutation) AppendOptions(value []map[string]interface{}) {
+ m.appendoptions = append(m.appendoptions, value...)
+}
+
+// AppendedOptions returns the list of values that were appended to the "options" field in this mutation.
+func (m *UserAttributeDefinitionMutation) AppendedOptions() ([]map[string]interface{}, bool) {
+ if len(m.appendoptions) == 0 {
+ return nil, false
+ }
+ return m.appendoptions, true
+}
+
+// ResetOptions resets all changes to the "options" field.
+func (m *UserAttributeDefinitionMutation) ResetOptions() {
+ m.options = nil
+ m.appendoptions = nil
+}
+
+// SetRequired sets the "required" field.
+func (m *UserAttributeDefinitionMutation) SetRequired(b bool) {
+ m.required = &b
+}
+
+// Required returns the value of the "required" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Required() (r bool, exists bool) {
+ v := m.required
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldRequired returns the old "required" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldRequired(ctx context.Context) (v bool, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldRequired is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldRequired requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldRequired: %w", err)
+ }
+ return oldValue.Required, nil
+}
+
+// ResetRequired resets all changes to the "required" field.
+func (m *UserAttributeDefinitionMutation) ResetRequired() {
+ m.required = nil
+}
+
+// SetValidation sets the "validation" field.
+func (m *UserAttributeDefinitionMutation) SetValidation(value map[string]interface{}) {
+ m.validation = &value
+}
+
+// Validation returns the value of the "validation" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Validation() (r map[string]interface{}, exists bool) {
+ v := m.validation
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldValidation returns the old "validation" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldValidation(ctx context.Context) (v map[string]interface{}, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldValidation is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldValidation requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldValidation: %w", err)
+ }
+ return oldValue.Validation, nil
+}
+
+// ResetValidation resets all changes to the "validation" field.
+func (m *UserAttributeDefinitionMutation) ResetValidation() {
+ m.validation = nil
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (m *UserAttributeDefinitionMutation) SetPlaceholder(s string) {
+ m.placeholder = &s
+}
+
+// Placeholder returns the value of the "placeholder" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Placeholder() (r string, exists bool) {
+ v := m.placeholder
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldPlaceholder returns the old "placeholder" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldPlaceholder(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldPlaceholder is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldPlaceholder requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldPlaceholder: %w", err)
+ }
+ return oldValue.Placeholder, nil
+}
+
+// ResetPlaceholder resets all changes to the "placeholder" field.
+func (m *UserAttributeDefinitionMutation) ResetPlaceholder() {
+ m.placeholder = nil
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (m *UserAttributeDefinitionMutation) SetDisplayOrder(i int) {
+ m.display_order = &i
+ m.adddisplay_order = nil
+}
+
+// DisplayOrder returns the value of the "display_order" field in the mutation.
+func (m *UserAttributeDefinitionMutation) DisplayOrder() (r int, exists bool) {
+ v := m.display_order
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldDisplayOrder returns the old "display_order" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldDisplayOrder(ctx context.Context) (v int, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldDisplayOrder is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldDisplayOrder requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldDisplayOrder: %w", err)
+ }
+ return oldValue.DisplayOrder, nil
+}
+
+// AddDisplayOrder adds i to the "display_order" field.
+func (m *UserAttributeDefinitionMutation) AddDisplayOrder(i int) {
+ if m.adddisplay_order != nil {
+ *m.adddisplay_order += i
+ } else {
+ m.adddisplay_order = &i
+ }
+}
+
+// AddedDisplayOrder returns the value that was added to the "display_order" field in this mutation.
+func (m *UserAttributeDefinitionMutation) AddedDisplayOrder() (r int, exists bool) {
+ v := m.adddisplay_order
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// ResetDisplayOrder resets all changes to the "display_order" field.
+func (m *UserAttributeDefinitionMutation) ResetDisplayOrder() {
+ m.display_order = nil
+ m.adddisplay_order = nil
+}
+
+// SetEnabled sets the "enabled" field.
+func (m *UserAttributeDefinitionMutation) SetEnabled(b bool) {
+ m.enabled = &b
+}
+
+// Enabled returns the value of the "enabled" field in the mutation.
+func (m *UserAttributeDefinitionMutation) Enabled() (r bool, exists bool) {
+ v := m.enabled
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldEnabled returns the old "enabled" field's value of the UserAttributeDefinition entity.
+// If the UserAttributeDefinition object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeDefinitionMutation) OldEnabled(ctx context.Context) (v bool, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldEnabled is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldEnabled requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldEnabled: %w", err)
+ }
+ return oldValue.Enabled, nil
+}
+
+// ResetEnabled resets all changes to the "enabled" field.
+func (m *UserAttributeDefinitionMutation) ResetEnabled() {
+ m.enabled = nil
+}
+
+// AddValueIDs adds the "values" edge to the UserAttributeValue entity by ids.
+func (m *UserAttributeDefinitionMutation) AddValueIDs(ids ...int64) {
+ if m.values == nil {
+ m.values = make(map[int64]struct{})
+ }
+ for i := range ids {
+ m.values[ids[i]] = struct{}{}
+ }
+}
+
+// ClearValues clears the "values" edge to the UserAttributeValue entity.
+func (m *UserAttributeDefinitionMutation) ClearValues() {
+ m.clearedvalues = true
+}
+
+// ValuesCleared reports if the "values" edge to the UserAttributeValue entity was cleared.
+func (m *UserAttributeDefinitionMutation) ValuesCleared() bool {
+ return m.clearedvalues
+}
+
+// RemoveValueIDs removes the "values" edge to the UserAttributeValue entity by IDs.
+func (m *UserAttributeDefinitionMutation) RemoveValueIDs(ids ...int64) {
+ if m.removedvalues == nil {
+ m.removedvalues = make(map[int64]struct{})
+ }
+ for i := range ids {
+ delete(m.values, ids[i])
+ m.removedvalues[ids[i]] = struct{}{}
+ }
+}
+
+// RemovedValues returns the removed IDs of the "values" edge to the UserAttributeValue entity.
+func (m *UserAttributeDefinitionMutation) RemovedValuesIDs() (ids []int64) {
+ for id := range m.removedvalues {
+ ids = append(ids, id)
+ }
+ return
+}
+
+// ValuesIDs returns the "values" edge IDs in the mutation.
+func (m *UserAttributeDefinitionMutation) ValuesIDs() (ids []int64) {
+ for id := range m.values {
+ ids = append(ids, id)
+ }
+ return
+}
+
+// ResetValues resets all changes to the "values" edge.
+func (m *UserAttributeDefinitionMutation) ResetValues() {
+ m.values = nil
+ m.clearedvalues = false
+ m.removedvalues = nil
+}
+
+// Where appends a list predicates to the UserAttributeDefinitionMutation builder.
+func (m *UserAttributeDefinitionMutation) Where(ps ...predicate.UserAttributeDefinition) {
+ m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the UserAttributeDefinitionMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *UserAttributeDefinitionMutation) WhereP(ps ...func(*sql.Selector)) {
+ p := make([]predicate.UserAttributeDefinition, len(ps))
+ for i := range ps {
+ p[i] = ps[i]
+ }
+ m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *UserAttributeDefinitionMutation) Op() Op {
+ return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *UserAttributeDefinitionMutation) SetOp(op Op) {
+ m.op = op
+}
+
+// Type returns the node type of this mutation (UserAttributeDefinition).
+func (m *UserAttributeDefinitionMutation) Type() string {
+ return m.typ
+}
+
+// Fields returns all fields that were changed during this mutation. Note that in
+// order to get all numeric fields that were incremented/decremented, call
+// AddedFields().
+func (m *UserAttributeDefinitionMutation) Fields() []string {
+ fields := make([]string, 0, 13)
+ if m.created_at != nil {
+ fields = append(fields, userattributedefinition.FieldCreatedAt)
+ }
+ if m.updated_at != nil {
+ fields = append(fields, userattributedefinition.FieldUpdatedAt)
+ }
+ if m.deleted_at != nil {
+ fields = append(fields, userattributedefinition.FieldDeletedAt)
+ }
+ if m.key != nil {
+ fields = append(fields, userattributedefinition.FieldKey)
+ }
+ if m.name != nil {
+ fields = append(fields, userattributedefinition.FieldName)
+ }
+ if m.description != nil {
+ fields = append(fields, userattributedefinition.FieldDescription)
+ }
+ if m._type != nil {
+ fields = append(fields, userattributedefinition.FieldType)
+ }
+ if m.options != nil {
+ fields = append(fields, userattributedefinition.FieldOptions)
+ }
+ if m.required != nil {
+ fields = append(fields, userattributedefinition.FieldRequired)
+ }
+ if m.validation != nil {
+ fields = append(fields, userattributedefinition.FieldValidation)
+ }
+ if m.placeholder != nil {
+ fields = append(fields, userattributedefinition.FieldPlaceholder)
+ }
+ if m.display_order != nil {
+ fields = append(fields, userattributedefinition.FieldDisplayOrder)
+ }
+ if m.enabled != nil {
+ fields = append(fields, userattributedefinition.FieldEnabled)
+ }
+ return fields
+}
+
+// Field returns the value of a field with the given name. The second boolean
+// return value indicates that this field was not set, or was not defined in the
+// schema.
+func (m *UserAttributeDefinitionMutation) Field(name string) (ent.Value, bool) {
+ switch name {
+ case userattributedefinition.FieldCreatedAt:
+ return m.CreatedAt()
+ case userattributedefinition.FieldUpdatedAt:
+ return m.UpdatedAt()
+ case userattributedefinition.FieldDeletedAt:
+ return m.DeletedAt()
+ case userattributedefinition.FieldKey:
+ return m.Key()
+ case userattributedefinition.FieldName:
+ return m.Name()
+ case userattributedefinition.FieldDescription:
+ return m.Description()
+ case userattributedefinition.FieldType:
+ return m.GetType()
+ case userattributedefinition.FieldOptions:
+ return m.Options()
+ case userattributedefinition.FieldRequired:
+ return m.Required()
+ case userattributedefinition.FieldValidation:
+ return m.Validation()
+ case userattributedefinition.FieldPlaceholder:
+ return m.Placeholder()
+ case userattributedefinition.FieldDisplayOrder:
+ return m.DisplayOrder()
+ case userattributedefinition.FieldEnabled:
+ return m.Enabled()
+ }
+ return nil, false
+}
+
+// OldField returns the old value of the field from the database. An error is
+// returned if the mutation operation is not UpdateOne, or the query to the
+// database failed.
+func (m *UserAttributeDefinitionMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+ switch name {
+ case userattributedefinition.FieldCreatedAt:
+ return m.OldCreatedAt(ctx)
+ case userattributedefinition.FieldUpdatedAt:
+ return m.OldUpdatedAt(ctx)
+ case userattributedefinition.FieldDeletedAt:
+ return m.OldDeletedAt(ctx)
+ case userattributedefinition.FieldKey:
+ return m.OldKey(ctx)
+ case userattributedefinition.FieldName:
+ return m.OldName(ctx)
+ case userattributedefinition.FieldDescription:
+ return m.OldDescription(ctx)
+ case userattributedefinition.FieldType:
+ return m.OldType(ctx)
+ case userattributedefinition.FieldOptions:
+ return m.OldOptions(ctx)
+ case userattributedefinition.FieldRequired:
+ return m.OldRequired(ctx)
+ case userattributedefinition.FieldValidation:
+ return m.OldValidation(ctx)
+ case userattributedefinition.FieldPlaceholder:
+ return m.OldPlaceholder(ctx)
+ case userattributedefinition.FieldDisplayOrder:
+ return m.OldDisplayOrder(ctx)
+ case userattributedefinition.FieldEnabled:
+ return m.OldEnabled(ctx)
+ }
+ return nil, fmt.Errorf("unknown UserAttributeDefinition field %s", name)
+}
+
+// SetField sets the value of a field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserAttributeDefinitionMutation) SetField(name string, value ent.Value) error {
+ switch name {
+ case userattributedefinition.FieldCreatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetCreatedAt(v)
+ return nil
+ case userattributedefinition.FieldUpdatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUpdatedAt(v)
+ return nil
+ case userattributedefinition.FieldDeletedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetDeletedAt(v)
+ return nil
+ case userattributedefinition.FieldKey:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetKey(v)
+ return nil
+ case userattributedefinition.FieldName:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetName(v)
+ return nil
+ case userattributedefinition.FieldDescription:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetDescription(v)
+ return nil
+ case userattributedefinition.FieldType:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetType(v)
+ return nil
+ case userattributedefinition.FieldOptions:
+ v, ok := value.([]map[string]interface{})
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetOptions(v)
+ return nil
+ case userattributedefinition.FieldRequired:
+ v, ok := value.(bool)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetRequired(v)
+ return nil
+ case userattributedefinition.FieldValidation:
+ v, ok := value.(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetValidation(v)
+ return nil
+ case userattributedefinition.FieldPlaceholder:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetPlaceholder(v)
+ return nil
+ case userattributedefinition.FieldDisplayOrder:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetDisplayOrder(v)
+ return nil
+ case userattributedefinition.FieldEnabled:
+ v, ok := value.(bool)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetEnabled(v)
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *UserAttributeDefinitionMutation) AddedFields() []string {
+ var fields []string
+ if m.adddisplay_order != nil {
+ fields = append(fields, userattributedefinition.FieldDisplayOrder)
+ }
+ return fields
+}
+
+// AddedField returns the numeric value that was incremented/decremented on a field
+// with the given name. The second boolean return value indicates that this field
+// was not set, or was not defined in the schema.
+func (m *UserAttributeDefinitionMutation) AddedField(name string) (ent.Value, bool) {
+ switch name {
+ case userattributedefinition.FieldDisplayOrder:
+ return m.AddedDisplayOrder()
+ }
+ return nil, false
+}
+
+// AddField adds the value to the field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserAttributeDefinitionMutation) AddField(name string, value ent.Value) error {
+ switch name {
+ case userattributedefinition.FieldDisplayOrder:
+ v, ok := value.(int)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.AddDisplayOrder(v)
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *UserAttributeDefinitionMutation) ClearedFields() []string {
+ var fields []string
+ if m.FieldCleared(userattributedefinition.FieldDeletedAt) {
+ fields = append(fields, userattributedefinition.FieldDeletedAt)
+ }
+ return fields
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *UserAttributeDefinitionMutation) FieldCleared(name string) bool {
+ _, ok := m.clearedFields[name]
+ return ok
+}
+
+// ClearField clears the value of the field with the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *UserAttributeDefinitionMutation) ClearField(name string) error {
+ switch name {
+ case userattributedefinition.FieldDeletedAt:
+ m.ClearDeletedAt()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation for the field with the given name.
+// It returns an error if the field is not defined in the schema.
+func (m *UserAttributeDefinitionMutation) ResetField(name string) error {
+ switch name {
+ case userattributedefinition.FieldCreatedAt:
+ m.ResetCreatedAt()
+ return nil
+ case userattributedefinition.FieldUpdatedAt:
+ m.ResetUpdatedAt()
+ return nil
+ case userattributedefinition.FieldDeletedAt:
+ m.ResetDeletedAt()
+ return nil
+ case userattributedefinition.FieldKey:
+ m.ResetKey()
+ return nil
+ case userattributedefinition.FieldName:
+ m.ResetName()
+ return nil
+ case userattributedefinition.FieldDescription:
+ m.ResetDescription()
+ return nil
+ case userattributedefinition.FieldType:
+ m.ResetType()
+ return nil
+ case userattributedefinition.FieldOptions:
+ m.ResetOptions()
+ return nil
+ case userattributedefinition.FieldRequired:
+ m.ResetRequired()
+ return nil
+ case userattributedefinition.FieldValidation:
+ m.ResetValidation()
+ return nil
+ case userattributedefinition.FieldPlaceholder:
+ m.ResetPlaceholder()
+ return nil
+ case userattributedefinition.FieldDisplayOrder:
+ m.ResetDisplayOrder()
+ return nil
+ case userattributedefinition.FieldEnabled:
+ m.ResetEnabled()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *UserAttributeDefinitionMutation) AddedEdges() []string {
+ edges := make([]string, 0, 1)
+ if m.values != nil {
+ edges = append(edges, userattributedefinition.EdgeValues)
+ }
+ return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *UserAttributeDefinitionMutation) AddedIDs(name string) []ent.Value {
+ switch name {
+ case userattributedefinition.EdgeValues:
+ ids := make([]ent.Value, 0, len(m.values))
+ for id := range m.values {
+ ids = append(ids, id)
+ }
+ return ids
+ }
+ return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *UserAttributeDefinitionMutation) RemovedEdges() []string {
+ edges := make([]string, 0, 1)
+ if m.removedvalues != nil {
+ edges = append(edges, userattributedefinition.EdgeValues)
+ }
+ return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *UserAttributeDefinitionMutation) RemovedIDs(name string) []ent.Value {
+ switch name {
+ case userattributedefinition.EdgeValues:
+ ids := make([]ent.Value, 0, len(m.removedvalues))
+ for id := range m.removedvalues {
+ ids = append(ids, id)
+ }
+ return ids
+ }
+ return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *UserAttributeDefinitionMutation) ClearedEdges() []string {
+ edges := make([]string, 0, 1)
+ if m.clearedvalues {
+ edges = append(edges, userattributedefinition.EdgeValues)
+ }
+ return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *UserAttributeDefinitionMutation) EdgeCleared(name string) bool {
+ switch name {
+ case userattributedefinition.EdgeValues:
+ return m.clearedvalues
+ }
+ return false
+}
+
+// ClearEdge clears the value of the edge with the given name. It returns an error
+// if that edge is not defined in the schema.
+func (m *UserAttributeDefinitionMutation) ClearEdge(name string) error {
+ switch name {
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition unique edge %s", name)
+}
+
+// ResetEdge resets all changes to the edge with the given name in this mutation.
+// It returns an error if the edge is not defined in the schema.
+func (m *UserAttributeDefinitionMutation) ResetEdge(name string) error {
+ switch name {
+ case userattributedefinition.EdgeValues:
+ m.ResetValues()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeDefinition edge %s", name)
+}
+
+// UserAttributeValueMutation represents an operation that mutates the UserAttributeValue nodes in the graph.
+type UserAttributeValueMutation struct {
+ config
+ op Op
+ typ string
+ id *int64
+ created_at *time.Time
+ updated_at *time.Time
+ value *string
+ clearedFields map[string]struct{}
+ user *int64
+ cleareduser bool
+ definition *int64
+ cleareddefinition bool
+ done bool
+ oldValue func(context.Context) (*UserAttributeValue, error)
+ predicates []predicate.UserAttributeValue
+}
+
+var _ ent.Mutation = (*UserAttributeValueMutation)(nil)
+
+// userattributevalueOption allows management of the mutation configuration using functional options.
+type userattributevalueOption func(*UserAttributeValueMutation)
+
+// newUserAttributeValueMutation creates new mutation for the UserAttributeValue entity.
+func newUserAttributeValueMutation(c config, op Op, opts ...userattributevalueOption) *UserAttributeValueMutation {
+ m := &UserAttributeValueMutation{
+ config: c,
+ op: op,
+ typ: TypeUserAttributeValue,
+ clearedFields: make(map[string]struct{}),
+ }
+ for _, opt := range opts {
+ opt(m)
+ }
+ return m
+}
+
+// withUserAttributeValueID sets the ID field of the mutation.
+func withUserAttributeValueID(id int64) userattributevalueOption {
+ return func(m *UserAttributeValueMutation) {
+ var (
+ err error
+ once sync.Once
+ value *UserAttributeValue
+ )
+ m.oldValue = func(ctx context.Context) (*UserAttributeValue, error) {
+ once.Do(func() {
+ if m.done {
+ err = errors.New("querying old values post mutation is not allowed")
+ } else {
+ value, err = m.Client().UserAttributeValue.Get(ctx, id)
+ }
+ })
+ return value, err
+ }
+ m.id = &id
+ }
+}
+
+// withUserAttributeValue sets the old UserAttributeValue of the mutation.
+func withUserAttributeValue(node *UserAttributeValue) userattributevalueOption {
+ return func(m *UserAttributeValueMutation) {
+ m.oldValue = func(context.Context) (*UserAttributeValue, error) {
+ return node, nil
+ }
+ m.id = &node.ID
+ }
+}
+
+// Client returns a new `ent.Client` from the mutation. If the mutation was
+// executed in a transaction (ent.Tx), a transactional client is returned.
+func (m UserAttributeValueMutation) Client() *Client {
+ client := &Client{config: m.config}
+ client.init()
+ return client
+}
+
+// Tx returns an `ent.Tx` for mutations that were executed in transactions;
+// it returns an error otherwise.
+func (m UserAttributeValueMutation) Tx() (*Tx, error) {
+ if _, ok := m.driver.(*txDriver); !ok {
+ return nil, errors.New("ent: mutation is not running in a transaction")
+ }
+ tx := &Tx{config: m.config}
+ tx.init()
+ return tx, nil
+}
+
+// ID returns the ID value in the mutation. Note that the ID is only available
+// if it was provided to the builder or after it was returned from the database.
+func (m *UserAttributeValueMutation) ID() (id int64, exists bool) {
+ if m.id == nil {
+ return
+ }
+ return *m.id, true
+}
+
+// IDs queries the database and returns the entity ids that match the mutation's predicate.
+// That means, if the mutation is applied within a transaction with an isolation level such
+// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
+// or updated by the mutation.
+func (m *UserAttributeValueMutation) IDs(ctx context.Context) ([]int64, error) {
+ switch {
+ case m.op.Is(OpUpdateOne | OpDeleteOne):
+ id, exists := m.ID()
+ if exists {
+ return []int64{id}, nil
+ }
+ fallthrough
+ case m.op.Is(OpUpdate | OpDelete):
+ return m.Client().UserAttributeValue.Query().Where(m.predicates...).IDs(ctx)
+ default:
+ return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
+ }
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (m *UserAttributeValueMutation) SetCreatedAt(t time.Time) {
+ m.created_at = &t
+}
+
+// CreatedAt returns the value of the "created_at" field in the mutation.
+func (m *UserAttributeValueMutation) CreatedAt() (r time.Time, exists bool) {
+ v := m.created_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldCreatedAt returns the old "created_at" field's value of the UserAttributeValue entity.
+// If the UserAttributeValue object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeValueMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldCreatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err)
+ }
+ return oldValue.CreatedAt, nil
+}
+
+// ResetCreatedAt resets all changes to the "created_at" field.
+func (m *UserAttributeValueMutation) ResetCreatedAt() {
+ m.created_at = nil
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (m *UserAttributeValueMutation) SetUpdatedAt(t time.Time) {
+ m.updated_at = &t
+}
+
+// UpdatedAt returns the value of the "updated_at" field in the mutation.
+func (m *UserAttributeValueMutation) UpdatedAt() (r time.Time, exists bool) {
+ v := m.updated_at
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUpdatedAt returns the old "updated_at" field's value of the UserAttributeValue entity.
+// If the UserAttributeValue object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeValueMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUpdatedAt requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err)
+ }
+ return oldValue.UpdatedAt, nil
+}
+
+// ResetUpdatedAt resets all changes to the "updated_at" field.
+func (m *UserAttributeValueMutation) ResetUpdatedAt() {
+ m.updated_at = nil
+}
+
+// SetUserID sets the "user_id" field.
+func (m *UserAttributeValueMutation) SetUserID(i int64) {
+ m.user = &i
+}
+
+// UserID returns the value of the "user_id" field in the mutation.
+func (m *UserAttributeValueMutation) UserID() (r int64, exists bool) {
+ v := m.user
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldUserID returns the old "user_id" field's value of the UserAttributeValue entity.
+// If the UserAttributeValue object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeValueMutation) OldUserID(ctx context.Context) (v int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldUserID is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldUserID requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldUserID: %w", err)
+ }
+ return oldValue.UserID, nil
+}
+
+// ResetUserID resets all changes to the "user_id" field.
+func (m *UserAttributeValueMutation) ResetUserID() {
+ m.user = nil
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (m *UserAttributeValueMutation) SetAttributeID(i int64) {
+ m.definition = &i
+}
+
+// AttributeID returns the value of the "attribute_id" field in the mutation.
+func (m *UserAttributeValueMutation) AttributeID() (r int64, exists bool) {
+ v := m.definition
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldAttributeID returns the old "attribute_id" field's value of the UserAttributeValue entity.
+// If the UserAttributeValue object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeValueMutation) OldAttributeID(ctx context.Context) (v int64, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldAttributeID is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldAttributeID requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldAttributeID: %w", err)
+ }
+ return oldValue.AttributeID, nil
+}
+
+// ResetAttributeID resets all changes to the "attribute_id" field.
+func (m *UserAttributeValueMutation) ResetAttributeID() {
+ m.definition = nil
+}
+
+// SetValue sets the "value" field.
+func (m *UserAttributeValueMutation) SetValue(s string) {
+ m.value = &s
+}
+
+// Value returns the value of the "value" field in the mutation.
+func (m *UserAttributeValueMutation) Value() (r string, exists bool) {
+ v := m.value
+ if v == nil {
+ return
+ }
+ return *v, true
+}
+
+// OldValue returns the old "value" field's value of the UserAttributeValue entity.
+// If the UserAttributeValue object wasn't provided to the builder, the object is fetched from the database.
+// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
+func (m *UserAttributeValueMutation) OldValue(ctx context.Context) (v string, err error) {
+ if !m.op.Is(OpUpdateOne) {
+ return v, errors.New("OldValue is only allowed on UpdateOne operations")
+ }
+ if m.id == nil || m.oldValue == nil {
+ return v, errors.New("OldValue requires an ID field in the mutation")
+ }
+ oldValue, err := m.oldValue(ctx)
+ if err != nil {
+ return v, fmt.Errorf("querying old value for OldValue: %w", err)
+ }
+ return oldValue.Value, nil
+}
+
+// ResetValue resets all changes to the "value" field.
+func (m *UserAttributeValueMutation) ResetValue() {
+ m.value = nil
+}
+
+// ClearUser clears the "user" edge to the User entity.
+func (m *UserAttributeValueMutation) ClearUser() {
+ m.cleareduser = true
+ m.clearedFields[userattributevalue.FieldUserID] = struct{}{}
+}
+
+// UserCleared reports if the "user" edge to the User entity was cleared.
+func (m *UserAttributeValueMutation) UserCleared() bool {
+ return m.cleareduser
+}
+
+// UserIDs returns the "user" edge IDs in the mutation.
+// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
+// UserID instead. It exists only for internal usage by the builders.
+func (m *UserAttributeValueMutation) UserIDs() (ids []int64) {
+ if id := m.user; id != nil {
+ ids = append(ids, *id)
+ }
+ return
+}
+
+// ResetUser resets all changes to the "user" edge.
+func (m *UserAttributeValueMutation) ResetUser() {
+ m.user = nil
+ m.cleareduser = false
+}
+
+// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by id.
+func (m *UserAttributeValueMutation) SetDefinitionID(id int64) {
+ m.definition = &id
+}
+
+// ClearDefinition clears the "definition" edge to the UserAttributeDefinition entity.
+func (m *UserAttributeValueMutation) ClearDefinition() {
+ m.cleareddefinition = true
+ m.clearedFields[userattributevalue.FieldAttributeID] = struct{}{}
+}
+
+// DefinitionCleared reports if the "definition" edge to the UserAttributeDefinition entity was cleared.
+func (m *UserAttributeValueMutation) DefinitionCleared() bool {
+ return m.cleareddefinition
+}
+
+// DefinitionID returns the "definition" edge ID in the mutation.
+func (m *UserAttributeValueMutation) DefinitionID() (id int64, exists bool) {
+ if m.definition != nil {
+ return *m.definition, true
+ }
+ return
+}
+
+// DefinitionIDs returns the "definition" edge IDs in the mutation.
+// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
+// DefinitionID instead. It exists only for internal usage by the builders.
+func (m *UserAttributeValueMutation) DefinitionIDs() (ids []int64) {
+ if id := m.definition; id != nil {
+ ids = append(ids, *id)
+ }
+ return
+}
+
+// ResetDefinition resets all changes to the "definition" edge.
+func (m *UserAttributeValueMutation) ResetDefinition() {
+ m.definition = nil
+ m.cleareddefinition = false
+}
+
+// Where appends a list predicates to the UserAttributeValueMutation builder.
+func (m *UserAttributeValueMutation) Where(ps ...predicate.UserAttributeValue) {
+ m.predicates = append(m.predicates, ps...)
+}
+
+// WhereP appends storage-level predicates to the UserAttributeValueMutation builder. Using this method,
+// users can use type-assertion to append predicates that do not depend on any generated package.
+func (m *UserAttributeValueMutation) WhereP(ps ...func(*sql.Selector)) {
+ p := make([]predicate.UserAttributeValue, len(ps))
+ for i := range ps {
+ p[i] = ps[i]
+ }
+ m.Where(p...)
+}
+
+// Op returns the operation name.
+func (m *UserAttributeValueMutation) Op() Op {
+ return m.op
+}
+
+// SetOp allows setting the mutation operation.
+func (m *UserAttributeValueMutation) SetOp(op Op) {
+ m.op = op
+}
+
+// Type returns the node type of this mutation (UserAttributeValue).
+func (m *UserAttributeValueMutation) Type() string {
+ return m.typ
+}
+
+// Fields returns all fields that were changed during this mutation. Note that in
+// order to get all numeric fields that were incremented/decremented, call
+// AddedFields().
+func (m *UserAttributeValueMutation) Fields() []string {
+ fields := make([]string, 0, 5)
+ if m.created_at != nil {
+ fields = append(fields, userattributevalue.FieldCreatedAt)
+ }
+ if m.updated_at != nil {
+ fields = append(fields, userattributevalue.FieldUpdatedAt)
+ }
+ if m.user != nil {
+ fields = append(fields, userattributevalue.FieldUserID)
+ }
+ if m.definition != nil {
+ fields = append(fields, userattributevalue.FieldAttributeID)
+ }
+ if m.value != nil {
+ fields = append(fields, userattributevalue.FieldValue)
+ }
+ return fields
+}
+
+// Field returns the value of a field with the given name. The second boolean
+// return value indicates that this field was not set, or was not defined in the
+// schema.
+func (m *UserAttributeValueMutation) Field(name string) (ent.Value, bool) {
+ switch name {
+ case userattributevalue.FieldCreatedAt:
+ return m.CreatedAt()
+ case userattributevalue.FieldUpdatedAt:
+ return m.UpdatedAt()
+ case userattributevalue.FieldUserID:
+ return m.UserID()
+ case userattributevalue.FieldAttributeID:
+ return m.AttributeID()
+ case userattributevalue.FieldValue:
+ return m.Value()
+ }
+ return nil, false
+}
+
+// OldField returns the old value of the field from the database. An error is
+// returned if the mutation operation is not UpdateOne, or the query to the
+// database failed.
+func (m *UserAttributeValueMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
+ switch name {
+ case userattributevalue.FieldCreatedAt:
+ return m.OldCreatedAt(ctx)
+ case userattributevalue.FieldUpdatedAt:
+ return m.OldUpdatedAt(ctx)
+ case userattributevalue.FieldUserID:
+ return m.OldUserID(ctx)
+ case userattributevalue.FieldAttributeID:
+ return m.OldAttributeID(ctx)
+ case userattributevalue.FieldValue:
+ return m.OldValue(ctx)
+ }
+ return nil, fmt.Errorf("unknown UserAttributeValue field %s", name)
+}
+
+// SetField sets the value of a field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserAttributeValueMutation) SetField(name string, value ent.Value) error {
+ switch name {
+ case userattributevalue.FieldCreatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetCreatedAt(v)
+ return nil
+ case userattributevalue.FieldUpdatedAt:
+ v, ok := value.(time.Time)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUpdatedAt(v)
+ return nil
+ case userattributevalue.FieldUserID:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetUserID(v)
+ return nil
+ case userattributevalue.FieldAttributeID:
+ v, ok := value.(int64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetAttributeID(v)
+ return nil
+ case userattributevalue.FieldValue:
+ v, ok := value.(string)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field %s", value, name)
+ }
+ m.SetValue(v)
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeValue field %s", name)
+}
+
+// AddedFields returns all numeric fields that were incremented/decremented during
+// this mutation.
+func (m *UserAttributeValueMutation) AddedFields() []string {
+ var fields []string
+ return fields
+}
+
+// AddedField returns the numeric value that was incremented/decremented on a field
+// with the given name. The second boolean return value indicates that this field
+// was not set, or was not defined in the schema.
+func (m *UserAttributeValueMutation) AddedField(name string) (ent.Value, bool) {
+ switch name {
+ }
+ return nil, false
+}
+
+// AddField adds the value to the field with the given name. It returns an error if
+// the field is not defined in the schema, or if the type mismatched the field
+// type.
+func (m *UserAttributeValueMutation) AddField(name string, value ent.Value) error {
+ switch name {
+ }
+ return fmt.Errorf("unknown UserAttributeValue numeric field %s", name)
+}
+
+// ClearedFields returns all nullable fields that were cleared during this
+// mutation.
+func (m *UserAttributeValueMutation) ClearedFields() []string {
+ return nil
+}
+
+// FieldCleared returns a boolean indicating if a field with the given name was
+// cleared in this mutation.
+func (m *UserAttributeValueMutation) FieldCleared(name string) bool {
+ _, ok := m.clearedFields[name]
+ return ok
+}
+
+// ClearField clears the value of the field with the given name. It returns an
+// error if the field is not defined in the schema.
+func (m *UserAttributeValueMutation) ClearField(name string) error {
+ return fmt.Errorf("unknown UserAttributeValue nullable field %s", name)
+}
+
+// ResetField resets all changes in the mutation for the field with the given name.
+// It returns an error if the field is not defined in the schema.
+func (m *UserAttributeValueMutation) ResetField(name string) error {
+ switch name {
+ case userattributevalue.FieldCreatedAt:
+ m.ResetCreatedAt()
+ return nil
+ case userattributevalue.FieldUpdatedAt:
+ m.ResetUpdatedAt()
+ return nil
+ case userattributevalue.FieldUserID:
+ m.ResetUserID()
+ return nil
+ case userattributevalue.FieldAttributeID:
+ m.ResetAttributeID()
+ return nil
+ case userattributevalue.FieldValue:
+ m.ResetValue()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeValue field %s", name)
+}
+
+// AddedEdges returns all edge names that were set/added in this mutation.
+func (m *UserAttributeValueMutation) AddedEdges() []string {
+ edges := make([]string, 0, 2)
+ if m.user != nil {
+ edges = append(edges, userattributevalue.EdgeUser)
+ }
+ if m.definition != nil {
+ edges = append(edges, userattributevalue.EdgeDefinition)
+ }
+ return edges
+}
+
+// AddedIDs returns all IDs (to other nodes) that were added for the given edge
+// name in this mutation.
+func (m *UserAttributeValueMutation) AddedIDs(name string) []ent.Value {
+ switch name {
+ case userattributevalue.EdgeUser:
+ if id := m.user; id != nil {
+ return []ent.Value{*id}
+ }
+ case userattributevalue.EdgeDefinition:
+ if id := m.definition; id != nil {
+ return []ent.Value{*id}
+ }
+ }
+ return nil
+}
+
+// RemovedEdges returns all edge names that were removed in this mutation.
+func (m *UserAttributeValueMutation) RemovedEdges() []string {
+ edges := make([]string, 0, 2)
+ return edges
+}
+
+// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
+// the given name in this mutation.
+func (m *UserAttributeValueMutation) RemovedIDs(name string) []ent.Value {
+ return nil
+}
+
+// ClearedEdges returns all edge names that were cleared in this mutation.
+func (m *UserAttributeValueMutation) ClearedEdges() []string {
+ edges := make([]string, 0, 2)
+ if m.cleareduser {
+ edges = append(edges, userattributevalue.EdgeUser)
+ }
+ if m.cleareddefinition {
+ edges = append(edges, userattributevalue.EdgeDefinition)
+ }
+ return edges
+}
+
+// EdgeCleared returns a boolean which indicates if the edge with the given name
+// was cleared in this mutation.
+func (m *UserAttributeValueMutation) EdgeCleared(name string) bool {
+ switch name {
+ case userattributevalue.EdgeUser:
+ return m.cleareduser
+ case userattributevalue.EdgeDefinition:
+ return m.cleareddefinition
+ }
+ return false
+}
+
+// ClearEdge clears the value of the edge with the given name. It returns an error
+// if that edge is not defined in the schema.
+func (m *UserAttributeValueMutation) ClearEdge(name string) error {
+ switch name {
+ case userattributevalue.EdgeUser:
+ m.ClearUser()
+ return nil
+ case userattributevalue.EdgeDefinition:
+ m.ClearDefinition()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeValue unique edge %s", name)
+}
+
+// ResetEdge resets all changes to the edge with the given name in this mutation.
+// It returns an error if the edge is not defined in the schema.
+func (m *UserAttributeValueMutation) ResetEdge(name string) error {
+ switch name {
+ case userattributevalue.EdgeUser:
+ m.ResetUser()
+ return nil
+ case userattributevalue.EdgeDefinition:
+ m.ResetDefinition()
+ return nil
+ }
+ return fmt.Errorf("unknown UserAttributeValue edge %s", name)
+}
+
// UserSubscriptionMutation represents an operation that mutates the UserSubscription nodes in the graph.
type UserSubscriptionMutation struct {
config
diff --git a/backend/ent/predicate/predicate.go b/backend/ent/predicate/predicate.go
index f6bdf466..ae1bf007 100644
--- a/backend/ent/predicate/predicate.go
+++ b/backend/ent/predicate/predicate.go
@@ -36,5 +36,11 @@ type User func(*sql.Selector)
// UserAllowedGroup is the predicate function for userallowedgroup builders.
type UserAllowedGroup func(*sql.Selector)
+// UserAttributeDefinition is the predicate function for userattributedefinition builders.
+type UserAttributeDefinition func(*sql.Selector)
+
+// UserAttributeValue is the predicate function for userattributevalue builders.
+type UserAttributeValue func(*sql.Selector)
+
// UserSubscription is the predicate function for usersubscription builders.
type UserSubscription func(*sql.Selector)
diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go
index 0b254b3e..12c3e7e3 100644
--- a/backend/ent/runtime/runtime.go
+++ b/backend/ent/runtime/runtime.go
@@ -16,6 +16,8 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -604,14 +606,8 @@ func init() {
user.DefaultUsername = userDescUsername.Default.(string)
// user.UsernameValidator is a validator for the "username" field. It is called by the builders before save.
user.UsernameValidator = userDescUsername.Validators[0].(func(string) error)
- // userDescWechat is the schema descriptor for wechat field.
- userDescWechat := userFields[7].Descriptor()
- // user.DefaultWechat holds the default value on creation for the wechat field.
- user.DefaultWechat = userDescWechat.Default.(string)
- // user.WechatValidator is a validator for the "wechat" field. It is called by the builders before save.
- user.WechatValidator = userDescWechat.Validators[0].(func(string) error)
// userDescNotes is the schema descriptor for notes field.
- userDescNotes := userFields[8].Descriptor()
+ userDescNotes := userFields[7].Descriptor()
// user.DefaultNotes holds the default value on creation for the notes field.
user.DefaultNotes = userDescNotes.Default.(string)
userallowedgroupFields := schema.UserAllowedGroup{}.Fields()
@@ -620,6 +616,128 @@ func init() {
userallowedgroupDescCreatedAt := userallowedgroupFields[2].Descriptor()
// userallowedgroup.DefaultCreatedAt holds the default value on creation for the created_at field.
userallowedgroup.DefaultCreatedAt = userallowedgroupDescCreatedAt.Default.(func() time.Time)
+ userattributedefinitionMixin := schema.UserAttributeDefinition{}.Mixin()
+ userattributedefinitionMixinHooks1 := userattributedefinitionMixin[1].Hooks()
+ userattributedefinition.Hooks[0] = userattributedefinitionMixinHooks1[0]
+ userattributedefinitionMixinInters1 := userattributedefinitionMixin[1].Interceptors()
+ userattributedefinition.Interceptors[0] = userattributedefinitionMixinInters1[0]
+ userattributedefinitionMixinFields0 := userattributedefinitionMixin[0].Fields()
+ _ = userattributedefinitionMixinFields0
+ userattributedefinitionFields := schema.UserAttributeDefinition{}.Fields()
+ _ = userattributedefinitionFields
+ // userattributedefinitionDescCreatedAt is the schema descriptor for created_at field.
+ userattributedefinitionDescCreatedAt := userattributedefinitionMixinFields0[0].Descriptor()
+ // userattributedefinition.DefaultCreatedAt holds the default value on creation for the created_at field.
+ userattributedefinition.DefaultCreatedAt = userattributedefinitionDescCreatedAt.Default.(func() time.Time)
+ // userattributedefinitionDescUpdatedAt is the schema descriptor for updated_at field.
+ userattributedefinitionDescUpdatedAt := userattributedefinitionMixinFields0[1].Descriptor()
+ // userattributedefinition.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+ userattributedefinition.DefaultUpdatedAt = userattributedefinitionDescUpdatedAt.Default.(func() time.Time)
+ // userattributedefinition.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+ userattributedefinition.UpdateDefaultUpdatedAt = userattributedefinitionDescUpdatedAt.UpdateDefault.(func() time.Time)
+ // userattributedefinitionDescKey is the schema descriptor for key field.
+ userattributedefinitionDescKey := userattributedefinitionFields[0].Descriptor()
+ // userattributedefinition.KeyValidator is a validator for the "key" field. It is called by the builders before save.
+ userattributedefinition.KeyValidator = func() func(string) error {
+ validators := userattributedefinitionDescKey.Validators
+ fns := [...]func(string) error{
+ validators[0].(func(string) error),
+ validators[1].(func(string) error),
+ }
+ return func(key string) error {
+ for _, fn := range fns {
+ if err := fn(key); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ }()
+ // userattributedefinitionDescName is the schema descriptor for name field.
+ userattributedefinitionDescName := userattributedefinitionFields[1].Descriptor()
+ // userattributedefinition.NameValidator is a validator for the "name" field. It is called by the builders before save.
+ userattributedefinition.NameValidator = func() func(string) error {
+ validators := userattributedefinitionDescName.Validators
+ fns := [...]func(string) error{
+ validators[0].(func(string) error),
+ validators[1].(func(string) error),
+ }
+ return func(name string) error {
+ for _, fn := range fns {
+ if err := fn(name); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ }()
+ // userattributedefinitionDescDescription is the schema descriptor for description field.
+ userattributedefinitionDescDescription := userattributedefinitionFields[2].Descriptor()
+ // userattributedefinition.DefaultDescription holds the default value on creation for the description field.
+ userattributedefinition.DefaultDescription = userattributedefinitionDescDescription.Default.(string)
+ // userattributedefinitionDescType is the schema descriptor for type field.
+ userattributedefinitionDescType := userattributedefinitionFields[3].Descriptor()
+ // userattributedefinition.TypeValidator is a validator for the "type" field. It is called by the builders before save.
+ userattributedefinition.TypeValidator = func() func(string) error {
+ validators := userattributedefinitionDescType.Validators
+ fns := [...]func(string) error{
+ validators[0].(func(string) error),
+ validators[1].(func(string) error),
+ }
+ return func(_type string) error {
+ for _, fn := range fns {
+ if err := fn(_type); err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+ }()
+ // userattributedefinitionDescOptions is the schema descriptor for options field.
+ userattributedefinitionDescOptions := userattributedefinitionFields[4].Descriptor()
+ // userattributedefinition.DefaultOptions holds the default value on creation for the options field.
+ userattributedefinition.DefaultOptions = userattributedefinitionDescOptions.Default.([]map[string]interface{})
+ // userattributedefinitionDescRequired is the schema descriptor for required field.
+ userattributedefinitionDescRequired := userattributedefinitionFields[5].Descriptor()
+ // userattributedefinition.DefaultRequired holds the default value on creation for the required field.
+ userattributedefinition.DefaultRequired = userattributedefinitionDescRequired.Default.(bool)
+ // userattributedefinitionDescValidation is the schema descriptor for validation field.
+ userattributedefinitionDescValidation := userattributedefinitionFields[6].Descriptor()
+ // userattributedefinition.DefaultValidation holds the default value on creation for the validation field.
+ userattributedefinition.DefaultValidation = userattributedefinitionDescValidation.Default.(map[string]interface{})
+ // userattributedefinitionDescPlaceholder is the schema descriptor for placeholder field.
+ userattributedefinitionDescPlaceholder := userattributedefinitionFields[7].Descriptor()
+ // userattributedefinition.DefaultPlaceholder holds the default value on creation for the placeholder field.
+ userattributedefinition.DefaultPlaceholder = userattributedefinitionDescPlaceholder.Default.(string)
+ // userattributedefinition.PlaceholderValidator is a validator for the "placeholder" field. It is called by the builders before save.
+ userattributedefinition.PlaceholderValidator = userattributedefinitionDescPlaceholder.Validators[0].(func(string) error)
+ // userattributedefinitionDescDisplayOrder is the schema descriptor for display_order field.
+ userattributedefinitionDescDisplayOrder := userattributedefinitionFields[8].Descriptor()
+ // userattributedefinition.DefaultDisplayOrder holds the default value on creation for the display_order field.
+ userattributedefinition.DefaultDisplayOrder = userattributedefinitionDescDisplayOrder.Default.(int)
+ // userattributedefinitionDescEnabled is the schema descriptor for enabled field.
+ userattributedefinitionDescEnabled := userattributedefinitionFields[9].Descriptor()
+ // userattributedefinition.DefaultEnabled holds the default value on creation for the enabled field.
+ userattributedefinition.DefaultEnabled = userattributedefinitionDescEnabled.Default.(bool)
+ userattributevalueMixin := schema.UserAttributeValue{}.Mixin()
+ userattributevalueMixinFields0 := userattributevalueMixin[0].Fields()
+ _ = userattributevalueMixinFields0
+ userattributevalueFields := schema.UserAttributeValue{}.Fields()
+ _ = userattributevalueFields
+ // userattributevalueDescCreatedAt is the schema descriptor for created_at field.
+ userattributevalueDescCreatedAt := userattributevalueMixinFields0[0].Descriptor()
+ // userattributevalue.DefaultCreatedAt holds the default value on creation for the created_at field.
+ userattributevalue.DefaultCreatedAt = userattributevalueDescCreatedAt.Default.(func() time.Time)
+ // userattributevalueDescUpdatedAt is the schema descriptor for updated_at field.
+ userattributevalueDescUpdatedAt := userattributevalueMixinFields0[1].Descriptor()
+ // userattributevalue.DefaultUpdatedAt holds the default value on creation for the updated_at field.
+ userattributevalue.DefaultUpdatedAt = userattributevalueDescUpdatedAt.Default.(func() time.Time)
+ // userattributevalue.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field.
+ userattributevalue.UpdateDefaultUpdatedAt = userattributevalueDescUpdatedAt.UpdateDefault.(func() time.Time)
+ // userattributevalueDescValue is the schema descriptor for value field.
+ userattributevalueDescValue := userattributevalueFields[2].Descriptor()
+ // userattributevalue.DefaultValue holds the default value on creation for the value field.
+ userattributevalue.DefaultValue = userattributevalueDescValue.Default.(string)
usersubscriptionMixin := schema.UserSubscription{}.Mixin()
usersubscriptionMixinHooks1 := usersubscriptionMixin[1].Hooks()
usersubscription.Hooks[0] = usersubscriptionMixinHooks1[0]
diff --git a/backend/ent/schema/user_attribute_definition.go b/backend/ent/schema/user_attribute_definition.go
new file mode 100644
index 00000000..eb54171a
--- /dev/null
+++ b/backend/ent/schema/user_attribute_definition.go
@@ -0,0 +1,109 @@
+package schema
+
+import (
+ "github.com/Wei-Shaw/sub2api/ent/schema/mixins"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect"
+ "entgo.io/ent/dialect/entsql"
+ "entgo.io/ent/schema"
+ "entgo.io/ent/schema/edge"
+ "entgo.io/ent/schema/field"
+ "entgo.io/ent/schema/index"
+)
+
+// UserAttributeDefinition holds the schema definition for custom user attributes.
+//
+// This entity defines the metadata for user attributes, such as:
+// - Attribute key (unique identifier like "company_name")
+// - Display name shown in forms
+// - Field type (text, number, select, etc.)
+// - Validation rules
+// - Whether the field is required or enabled
+type UserAttributeDefinition struct {
+ ent.Schema
+}
+
+func (UserAttributeDefinition) Annotations() []schema.Annotation {
+ return []schema.Annotation{
+ entsql.Annotation{Table: "user_attribute_definitions"},
+ }
+}
+
+func (UserAttributeDefinition) Mixin() []ent.Mixin {
+ return []ent.Mixin{
+ mixins.TimeMixin{},
+ mixins.SoftDeleteMixin{},
+ }
+}
+
+func (UserAttributeDefinition) Fields() []ent.Field {
+ return []ent.Field{
+ // key: Unique identifier for the attribute (e.g., "company_name")
+ // Used for programmatic reference
+ field.String("key").
+ MaxLen(100).
+ NotEmpty(),
+
+ // name: Display name shown in forms (e.g., "Company Name")
+ field.String("name").
+ MaxLen(255).
+ NotEmpty(),
+
+ // description: Optional description/help text for the attribute
+ field.String("description").
+ SchemaType(map[string]string{dialect.Postgres: "text"}).
+ Default(""),
+
+ // type: Attribute type - text, textarea, number, email, url, date, select, multi_select
+ field.String("type").
+ MaxLen(20).
+ NotEmpty(),
+
+ // options: Select options for select/multi_select types (stored as JSONB)
+ // Format: [{"value": "xxx", "label": "XXX"}, ...]
+ field.JSON("options", []map[string]any{}).
+ Default([]map[string]any{}).
+ SchemaType(map[string]string{dialect.Postgres: "jsonb"}),
+
+ // required: Whether this attribute is required when editing a user
+ field.Bool("required").
+ Default(false),
+
+ // validation: Validation rules for the attribute value (stored as JSONB)
+ // Format: {"min_length": 1, "max_length": 100, "min": 0, "max": 100, "pattern": "^[a-z]+$", "message": "..."}
+ field.JSON("validation", map[string]any{}).
+ Default(map[string]any{}).
+ SchemaType(map[string]string{dialect.Postgres: "jsonb"}),
+
+ // placeholder: Placeholder text shown in input fields
+ field.String("placeholder").
+ MaxLen(255).
+ Default(""),
+
+ // display_order: Order in which attributes are displayed (lower = first)
+ field.Int("display_order").
+ Default(0),
+
+ // enabled: Whether this attribute is active and shown in forms
+ field.Bool("enabled").
+ Default(true),
+ }
+}
+
+func (UserAttributeDefinition) Edges() []ent.Edge {
+ return []ent.Edge{
+ // values: All user values for this attribute definition
+ edge.To("values", UserAttributeValue.Type),
+ }
+}
+
+func (UserAttributeDefinition) Indexes() []ent.Index {
+ return []ent.Index{
+ // Partial unique index on key (WHERE deleted_at IS NULL) via migration
+ index.Fields("key"),
+ index.Fields("enabled"),
+ index.Fields("display_order"),
+ index.Fields("deleted_at"),
+ }
+}
diff --git a/backend/ent/schema/user_attribute_value.go b/backend/ent/schema/user_attribute_value.go
new file mode 100644
index 00000000..fb9a9727
--- /dev/null
+++ b/backend/ent/schema/user_attribute_value.go
@@ -0,0 +1,74 @@
+package schema
+
+import (
+ "github.com/Wei-Shaw/sub2api/ent/schema/mixins"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/entsql"
+ "entgo.io/ent/schema"
+ "entgo.io/ent/schema/edge"
+ "entgo.io/ent/schema/field"
+ "entgo.io/ent/schema/index"
+)
+
+// UserAttributeValue holds a user's value for a specific attribute.
+//
+// This entity stores the actual values that users have for each attribute definition.
+// Values are stored as strings and converted to the appropriate type by the application.
+type UserAttributeValue struct {
+ ent.Schema
+}
+
+func (UserAttributeValue) Annotations() []schema.Annotation {
+ return []schema.Annotation{
+ entsql.Annotation{Table: "user_attribute_values"},
+ }
+}
+
+func (UserAttributeValue) Mixin() []ent.Mixin {
+ return []ent.Mixin{
+ // Only use TimeMixin, no soft delete - values are hard deleted
+ mixins.TimeMixin{},
+ }
+}
+
+func (UserAttributeValue) Fields() []ent.Field {
+ return []ent.Field{
+ // user_id: References the user this value belongs to
+ field.Int64("user_id"),
+
+ // attribute_id: References the attribute definition
+ field.Int64("attribute_id"),
+
+ // value: The actual value stored as a string
+ // For multi_select, this is a JSON array string
+ field.Text("value").
+ Default(""),
+ }
+}
+
+func (UserAttributeValue) Edges() []ent.Edge {
+ return []ent.Edge{
+ // user: The user who owns this attribute value
+ edge.From("user", User.Type).
+ Ref("attribute_values").
+ Field("user_id").
+ Required().
+ Unique(),
+
+ // definition: The attribute definition this value is for
+ edge.From("definition", UserAttributeDefinition.Type).
+ Ref("values").
+ Field("attribute_id").
+ Required().
+ Unique(),
+ }
+}
+
+func (UserAttributeValue) Indexes() []ent.Index {
+ return []ent.Index{
+ // Unique index on (user_id, attribute_id)
+ index.Fields("user_id", "attribute_id").Unique(),
+ index.Fields("attribute_id"),
+ }
+}
diff --git a/backend/ent/tx.go b/backend/ent/tx.go
index ecb0409d..b1bbdfc5 100644
--- a/backend/ent/tx.go
+++ b/backend/ent/tx.go
@@ -34,6 +34,10 @@ type Tx struct {
User *UserClient
// UserAllowedGroup is the client for interacting with the UserAllowedGroup builders.
UserAllowedGroup *UserAllowedGroupClient
+ // UserAttributeDefinition is the client for interacting with the UserAttributeDefinition builders.
+ UserAttributeDefinition *UserAttributeDefinitionClient
+ // UserAttributeValue is the client for interacting with the UserAttributeValue builders.
+ UserAttributeValue *UserAttributeValueClient
// UserSubscription is the client for interacting with the UserSubscription builders.
UserSubscription *UserSubscriptionClient
@@ -177,6 +181,8 @@ func (tx *Tx) init() {
tx.UsageLog = NewUsageLogClient(tx.config)
tx.User = NewUserClient(tx.config)
tx.UserAllowedGroup = NewUserAllowedGroupClient(tx.config)
+ tx.UserAttributeDefinition = NewUserAttributeDefinitionClient(tx.config)
+ tx.UserAttributeValue = NewUserAttributeValueClient(tx.config)
tx.UserSubscription = NewUserSubscriptionClient(tx.config)
}
diff --git a/backend/ent/userattributedefinition.go b/backend/ent/userattributedefinition.go
new file mode 100644
index 00000000..2ed86e4e
--- /dev/null
+++ b/backend/ent/userattributedefinition.go
@@ -0,0 +1,276 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+)
+
+// UserAttributeDefinition is the model entity for the UserAttributeDefinition schema.
+type UserAttributeDefinition struct {
+ config `json:"-"`
+ // ID of the ent.
+ ID int64 `json:"id,omitempty"`
+ // CreatedAt holds the value of the "created_at" field.
+ CreatedAt time.Time `json:"created_at,omitempty"`
+ // UpdatedAt holds the value of the "updated_at" field.
+ UpdatedAt time.Time `json:"updated_at,omitempty"`
+ // DeletedAt holds the value of the "deleted_at" field.
+ DeletedAt *time.Time `json:"deleted_at,omitempty"`
+ // Key holds the value of the "key" field.
+ Key string `json:"key,omitempty"`
+ // Name holds the value of the "name" field.
+ Name string `json:"name,omitempty"`
+ // Description holds the value of the "description" field.
+ Description string `json:"description,omitempty"`
+ // Type holds the value of the "type" field.
+ Type string `json:"type,omitempty"`
+ // Options holds the value of the "options" field.
+ Options []map[string]interface{} `json:"options,omitempty"`
+ // Required holds the value of the "required" field.
+ Required bool `json:"required,omitempty"`
+ // Validation holds the value of the "validation" field.
+ Validation map[string]interface{} `json:"validation,omitempty"`
+ // Placeholder holds the value of the "placeholder" field.
+ Placeholder string `json:"placeholder,omitempty"`
+ // DisplayOrder holds the value of the "display_order" field.
+ DisplayOrder int `json:"display_order,omitempty"`
+ // Enabled holds the value of the "enabled" field.
+ Enabled bool `json:"enabled,omitempty"`
+ // Edges holds the relations/edges for other nodes in the graph.
+ // The values are being populated by the UserAttributeDefinitionQuery when eager-loading is set.
+ Edges UserAttributeDefinitionEdges `json:"edges"`
+ selectValues sql.SelectValues
+}
+
+// UserAttributeDefinitionEdges holds the relations/edges for other nodes in the graph.
+type UserAttributeDefinitionEdges struct {
+ // Values holds the value of the values edge.
+ Values []*UserAttributeValue `json:"values,omitempty"`
+ // loadedTypes holds the information for reporting if a
+ // type was loaded (or requested) in eager-loading or not.
+ loadedTypes [1]bool
+}
+
+// ValuesOrErr returns the Values value or an error if the edge
+// was not loaded in eager-loading.
+func (e UserAttributeDefinitionEdges) ValuesOrErr() ([]*UserAttributeValue, error) {
+ if e.loadedTypes[0] {
+ return e.Values, nil
+ }
+ return nil, &NotLoadedError{edge: "values"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*UserAttributeDefinition) scanValues(columns []string) ([]any, error) {
+ values := make([]any, len(columns))
+ for i := range columns {
+ switch columns[i] {
+ case userattributedefinition.FieldOptions, userattributedefinition.FieldValidation:
+ values[i] = new([]byte)
+ case userattributedefinition.FieldRequired, userattributedefinition.FieldEnabled:
+ values[i] = new(sql.NullBool)
+ case userattributedefinition.FieldID, userattributedefinition.FieldDisplayOrder:
+ values[i] = new(sql.NullInt64)
+ case userattributedefinition.FieldKey, userattributedefinition.FieldName, userattributedefinition.FieldDescription, userattributedefinition.FieldType, userattributedefinition.FieldPlaceholder:
+ values[i] = new(sql.NullString)
+ case userattributedefinition.FieldCreatedAt, userattributedefinition.FieldUpdatedAt, userattributedefinition.FieldDeletedAt:
+ values[i] = new(sql.NullTime)
+ default:
+ values[i] = new(sql.UnknownType)
+ }
+ }
+ return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the UserAttributeDefinition fields.
+func (_m *UserAttributeDefinition) assignValues(columns []string, values []any) error {
+ if m, n := len(values), len(columns); m < n {
+ return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+ }
+ for i := range columns {
+ switch columns[i] {
+ case userattributedefinition.FieldID:
+ value, ok := values[i].(*sql.NullInt64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field id", value)
+ }
+ _m.ID = int64(value.Int64)
+ case userattributedefinition.FieldCreatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field created_at", values[i])
+ } else if value.Valid {
+ _m.CreatedAt = value.Time
+ }
+ case userattributedefinition.FieldUpdatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+ } else if value.Valid {
+ _m.UpdatedAt = value.Time
+ }
+ case userattributedefinition.FieldDeletedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field deleted_at", values[i])
+ } else if value.Valid {
+ _m.DeletedAt = new(time.Time)
+ *_m.DeletedAt = value.Time
+ }
+ case userattributedefinition.FieldKey:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field key", values[i])
+ } else if value.Valid {
+ _m.Key = value.String
+ }
+ case userattributedefinition.FieldName:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field name", values[i])
+ } else if value.Valid {
+ _m.Name = value.String
+ }
+ case userattributedefinition.FieldDescription:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field description", values[i])
+ } else if value.Valid {
+ _m.Description = value.String
+ }
+ case userattributedefinition.FieldType:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field type", values[i])
+ } else if value.Valid {
+ _m.Type = value.String
+ }
+ case userattributedefinition.FieldOptions:
+ if value, ok := values[i].(*[]byte); !ok {
+ return fmt.Errorf("unexpected type %T for field options", values[i])
+ } else if value != nil && len(*value) > 0 {
+ if err := json.Unmarshal(*value, &_m.Options); err != nil {
+ return fmt.Errorf("unmarshal field options: %w", err)
+ }
+ }
+ case userattributedefinition.FieldRequired:
+ if value, ok := values[i].(*sql.NullBool); !ok {
+ return fmt.Errorf("unexpected type %T for field required", values[i])
+ } else if value.Valid {
+ _m.Required = value.Bool
+ }
+ case userattributedefinition.FieldValidation:
+ if value, ok := values[i].(*[]byte); !ok {
+ return fmt.Errorf("unexpected type %T for field validation", values[i])
+ } else if value != nil && len(*value) > 0 {
+ if err := json.Unmarshal(*value, &_m.Validation); err != nil {
+ return fmt.Errorf("unmarshal field validation: %w", err)
+ }
+ }
+ case userattributedefinition.FieldPlaceholder:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field placeholder", values[i])
+ } else if value.Valid {
+ _m.Placeholder = value.String
+ }
+ case userattributedefinition.FieldDisplayOrder:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field display_order", values[i])
+ } else if value.Valid {
+ _m.DisplayOrder = int(value.Int64)
+ }
+ case userattributedefinition.FieldEnabled:
+ if value, ok := values[i].(*sql.NullBool); !ok {
+ return fmt.Errorf("unexpected type %T for field enabled", values[i])
+ } else if value.Valid {
+ _m.Enabled = value.Bool
+ }
+ default:
+ _m.selectValues.Set(columns[i], values[i])
+ }
+ }
+ return nil
+}
+
+// Value returns the ent.Value that was dynamically selected and assigned to the UserAttributeDefinition.
+// This includes values selected through modifiers, order, etc.
+func (_m *UserAttributeDefinition) Value(name string) (ent.Value, error) {
+ return _m.selectValues.Get(name)
+}
+
+// QueryValues queries the "values" edge of the UserAttributeDefinition entity.
+func (_m *UserAttributeDefinition) QueryValues() *UserAttributeValueQuery {
+ return NewUserAttributeDefinitionClient(_m.config).QueryValues(_m)
+}
+
+// Update returns a builder for updating this UserAttributeDefinition.
+// Note that you need to call UserAttributeDefinition.Unwrap() before calling this method if this UserAttributeDefinition
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (_m *UserAttributeDefinition) Update() *UserAttributeDefinitionUpdateOne {
+ return NewUserAttributeDefinitionClient(_m.config).UpdateOne(_m)
+}
+
+// Unwrap unwraps the UserAttributeDefinition entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
+func (_m *UserAttributeDefinition) Unwrap() *UserAttributeDefinition {
+ _tx, ok := _m.config.driver.(*txDriver)
+ if !ok {
+ panic("ent: UserAttributeDefinition is not a transactional entity")
+ }
+ _m.config.driver = _tx.drv
+ return _m
+}
+
+// String implements the fmt.Stringer.
+func (_m *UserAttributeDefinition) String() string {
+ var builder strings.Builder
+ builder.WriteString("UserAttributeDefinition(")
+ builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
+ builder.WriteString("created_at=")
+ builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("updated_at=")
+ builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ if v := _m.DeletedAt; v != nil {
+ builder.WriteString("deleted_at=")
+ builder.WriteString(v.Format(time.ANSIC))
+ }
+ builder.WriteString(", ")
+ builder.WriteString("key=")
+ builder.WriteString(_m.Key)
+ builder.WriteString(", ")
+ builder.WriteString("name=")
+ builder.WriteString(_m.Name)
+ builder.WriteString(", ")
+ builder.WriteString("description=")
+ builder.WriteString(_m.Description)
+ builder.WriteString(", ")
+ builder.WriteString("type=")
+ builder.WriteString(_m.Type)
+ builder.WriteString(", ")
+ builder.WriteString("options=")
+ builder.WriteString(fmt.Sprintf("%v", _m.Options))
+ builder.WriteString(", ")
+ builder.WriteString("required=")
+ builder.WriteString(fmt.Sprintf("%v", _m.Required))
+ builder.WriteString(", ")
+ builder.WriteString("validation=")
+ builder.WriteString(fmt.Sprintf("%v", _m.Validation))
+ builder.WriteString(", ")
+ builder.WriteString("placeholder=")
+ builder.WriteString(_m.Placeholder)
+ builder.WriteString(", ")
+ builder.WriteString("display_order=")
+ builder.WriteString(fmt.Sprintf("%v", _m.DisplayOrder))
+ builder.WriteString(", ")
+ builder.WriteString("enabled=")
+ builder.WriteString(fmt.Sprintf("%v", _m.Enabled))
+ builder.WriteByte(')')
+ return builder.String()
+}
+
+// UserAttributeDefinitions is a parsable slice of UserAttributeDefinition.
+type UserAttributeDefinitions []*UserAttributeDefinition
diff --git a/backend/ent/userattributedefinition/userattributedefinition.go b/backend/ent/userattributedefinition/userattributedefinition.go
new file mode 100644
index 00000000..ce398c03
--- /dev/null
+++ b/backend/ent/userattributedefinition/userattributedefinition.go
@@ -0,0 +1,205 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userattributedefinition
+
+import (
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+ // Label holds the string label denoting the userattributedefinition type in the database.
+ Label = "user_attribute_definition"
+ // FieldID holds the string denoting the id field in the database.
+ FieldID = "id"
+ // FieldCreatedAt holds the string denoting the created_at field in the database.
+ FieldCreatedAt = "created_at"
+ // FieldUpdatedAt holds the string denoting the updated_at field in the database.
+ FieldUpdatedAt = "updated_at"
+ // FieldDeletedAt holds the string denoting the deleted_at field in the database.
+ FieldDeletedAt = "deleted_at"
+ // FieldKey holds the string denoting the key field in the database.
+ FieldKey = "key"
+ // FieldName holds the string denoting the name field in the database.
+ FieldName = "name"
+ // FieldDescription holds the string denoting the description field in the database.
+ FieldDescription = "description"
+ // FieldType holds the string denoting the type field in the database.
+ FieldType = "type"
+ // FieldOptions holds the string denoting the options field in the database.
+ FieldOptions = "options"
+ // FieldRequired holds the string denoting the required field in the database.
+ FieldRequired = "required"
+ // FieldValidation holds the string denoting the validation field in the database.
+ FieldValidation = "validation"
+ // FieldPlaceholder holds the string denoting the placeholder field in the database.
+ FieldPlaceholder = "placeholder"
+ // FieldDisplayOrder holds the string denoting the display_order field in the database.
+ FieldDisplayOrder = "display_order"
+ // FieldEnabled holds the string denoting the enabled field in the database.
+ FieldEnabled = "enabled"
+ // EdgeValues holds the string denoting the values edge name in mutations.
+ EdgeValues = "values"
+ // Table holds the table name of the userattributedefinition in the database.
+ Table = "user_attribute_definitions"
+ // ValuesTable is the table that holds the values relation/edge.
+ ValuesTable = "user_attribute_values"
+ // ValuesInverseTable is the table name for the UserAttributeValue entity.
+ // It exists in this package in order to avoid circular dependency with the "userattributevalue" package.
+ ValuesInverseTable = "user_attribute_values"
+ // ValuesColumn is the table column denoting the values relation/edge.
+ ValuesColumn = "attribute_id"
+)
+
+// Columns holds all SQL columns for userattributedefinition fields.
+var Columns = []string{
+ FieldID,
+ FieldCreatedAt,
+ FieldUpdatedAt,
+ FieldDeletedAt,
+ FieldKey,
+ FieldName,
+ FieldDescription,
+ FieldType,
+ FieldOptions,
+ FieldRequired,
+ FieldValidation,
+ FieldPlaceholder,
+ FieldDisplayOrder,
+ FieldEnabled,
+}
+
+// ValidColumn reports if the column name is valid (part of the table columns).
+func ValidColumn(column string) bool {
+ for i := range Columns {
+ if column == Columns[i] {
+ return true
+ }
+ }
+ return false
+}
+
+// Note that the variables below are initialized by the runtime
+// package on the initialization of the application. Therefore,
+// it should be imported in the main as follows:
+//
+// import _ "github.com/Wei-Shaw/sub2api/ent/runtime"
+var (
+ Hooks [1]ent.Hook
+ Interceptors [1]ent.Interceptor
+ // DefaultCreatedAt holds the default value on creation for the "created_at" field.
+ DefaultCreatedAt func() time.Time
+ // DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
+ DefaultUpdatedAt func() time.Time
+ // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
+ UpdateDefaultUpdatedAt func() time.Time
+ // KeyValidator is a validator for the "key" field. It is called by the builders before save.
+ KeyValidator func(string) error
+ // NameValidator is a validator for the "name" field. It is called by the builders before save.
+ NameValidator func(string) error
+ // DefaultDescription holds the default value on creation for the "description" field.
+ DefaultDescription string
+ // TypeValidator is a validator for the "type" field. It is called by the builders before save.
+ TypeValidator func(string) error
+ // DefaultOptions holds the default value on creation for the "options" field.
+ DefaultOptions []map[string]interface{}
+ // DefaultRequired holds the default value on creation for the "required" field.
+ DefaultRequired bool
+ // DefaultValidation holds the default value on creation for the "validation" field.
+ DefaultValidation map[string]interface{}
+ // DefaultPlaceholder holds the default value on creation for the "placeholder" field.
+ DefaultPlaceholder string
+ // PlaceholderValidator is a validator for the "placeholder" field. It is called by the builders before save.
+ PlaceholderValidator func(string) error
+ // DefaultDisplayOrder holds the default value on creation for the "display_order" field.
+ DefaultDisplayOrder int
+ // DefaultEnabled holds the default value on creation for the "enabled" field.
+ DefaultEnabled bool
+)
+
+// OrderOption defines the ordering options for the UserAttributeDefinition queries.
+type OrderOption func(*sql.Selector)
+
+// ByID orders the results by the id field.
+func ByID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldID, opts...).ToFunc()
+}
+
+// ByCreatedAt orders the results by the created_at field.
+func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+}
+
+// ByUpdatedAt orders the results by the updated_at field.
+func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
+}
+
+// ByDeletedAt orders the results by the deleted_at field.
+func ByDeletedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldDeletedAt, opts...).ToFunc()
+}
+
+// ByKey orders the results by the key field.
+func ByKey(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldKey, opts...).ToFunc()
+}
+
+// ByName orders the results by the name field.
+func ByName(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldName, opts...).ToFunc()
+}
+
+// ByDescription orders the results by the description field.
+func ByDescription(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldDescription, opts...).ToFunc()
+}
+
+// ByType orders the results by the type field.
+func ByType(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldType, opts...).ToFunc()
+}
+
+// ByRequired orders the results by the required field.
+func ByRequired(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldRequired, opts...).ToFunc()
+}
+
+// ByPlaceholder orders the results by the placeholder field.
+func ByPlaceholder(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldPlaceholder, opts...).ToFunc()
+}
+
+// ByDisplayOrder orders the results by the display_order field.
+func ByDisplayOrder(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldDisplayOrder, opts...).ToFunc()
+}
+
+// ByEnabled orders the results by the enabled field.
+func ByEnabled(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldEnabled, opts...).ToFunc()
+}
+
+// ByValuesCount orders the results by values count.
+func ByValuesCount(opts ...sql.OrderTermOption) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborsCount(s, newValuesStep(), opts...)
+ }
+}
+
+// ByValues orders the results by values terms.
+func ByValues(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborTerms(s, newValuesStep(), append([]sql.OrderTerm{term}, terms...)...)
+ }
+}
+func newValuesStep() *sqlgraph.Step {
+ return sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.To(ValuesInverseTable, FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, ValuesTable, ValuesColumn),
+ )
+}
diff --git a/backend/ent/userattributedefinition/where.go b/backend/ent/userattributedefinition/where.go
new file mode 100644
index 00000000..7f4d06cb
--- /dev/null
+++ b/backend/ent/userattributedefinition/where.go
@@ -0,0 +1,664 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userattributedefinition
+
+import (
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int64) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldID, id))
+}
+
+// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
+func CreatedAt(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
+func UpdatedAt(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// DeletedAt applies equality check predicate on the "deleted_at" field. It's identical to DeletedAtEQ.
+func DeletedAt(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// Key applies equality check predicate on the "key" field. It's identical to KeyEQ.
+func Key(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldKey, v))
+}
+
+// Name applies equality check predicate on the "name" field. It's identical to NameEQ.
+func Name(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldName, v))
+}
+
+// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ.
+func Description(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDescription, v))
+}
+
+// Type applies equality check predicate on the "type" field. It's identical to TypeEQ.
+func Type(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldType, v))
+}
+
+// Required applies equality check predicate on the "required" field. It's identical to RequiredEQ.
+func Required(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldRequired, v))
+}
+
+// Placeholder applies equality check predicate on the "placeholder" field. It's identical to PlaceholderEQ.
+func Placeholder(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldPlaceholder, v))
+}
+
+// DisplayOrder applies equality check predicate on the "display_order" field. It's identical to DisplayOrderEQ.
+func DisplayOrder(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDisplayOrder, v))
+}
+
+// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ.
+func Enabled(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldEnabled, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// DeletedAtEQ applies the EQ predicate on the "deleted_at" field.
+func DeletedAtEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtNEQ applies the NEQ predicate on the "deleted_at" field.
+func DeletedAtNEQ(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDeletedAt, v))
+}
+
+// DeletedAtIn applies the In predicate on the "deleted_at" field.
+func DeletedAtIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtNotIn applies the NotIn predicate on the "deleted_at" field.
+func DeletedAtNotIn(vs ...time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDeletedAt, vs...))
+}
+
+// DeletedAtGT applies the GT predicate on the "deleted_at" field.
+func DeletedAtGT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldDeletedAt, v))
+}
+
+// DeletedAtGTE applies the GTE predicate on the "deleted_at" field.
+func DeletedAtGTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDeletedAt, v))
+}
+
+// DeletedAtLT applies the LT predicate on the "deleted_at" field.
+func DeletedAtLT(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldDeletedAt, v))
+}
+
+// DeletedAtLTE applies the LTE predicate on the "deleted_at" field.
+func DeletedAtLTE(v time.Time) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDeletedAt, v))
+}
+
+// DeletedAtIsNil applies the IsNil predicate on the "deleted_at" field.
+func DeletedAtIsNil() predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIsNull(FieldDeletedAt))
+}
+
+// DeletedAtNotNil applies the NotNil predicate on the "deleted_at" field.
+func DeletedAtNotNil() predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotNull(FieldDeletedAt))
+}
+
+// KeyEQ applies the EQ predicate on the "key" field.
+func KeyEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldKey, v))
+}
+
+// KeyNEQ applies the NEQ predicate on the "key" field.
+func KeyNEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldKey, v))
+}
+
+// KeyIn applies the In predicate on the "key" field.
+func KeyIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldKey, vs...))
+}
+
+// KeyNotIn applies the NotIn predicate on the "key" field.
+func KeyNotIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldKey, vs...))
+}
+
+// KeyGT applies the GT predicate on the "key" field.
+func KeyGT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldKey, v))
+}
+
+// KeyGTE applies the GTE predicate on the "key" field.
+func KeyGTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldKey, v))
+}
+
+// KeyLT applies the LT predicate on the "key" field.
+func KeyLT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldKey, v))
+}
+
+// KeyLTE applies the LTE predicate on the "key" field.
+func KeyLTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldKey, v))
+}
+
+// KeyContains applies the Contains predicate on the "key" field.
+func KeyContains(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContains(FieldKey, v))
+}
+
+// KeyHasPrefix applies the HasPrefix predicate on the "key" field.
+func KeyHasPrefix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldKey, v))
+}
+
+// KeyHasSuffix applies the HasSuffix predicate on the "key" field.
+func KeyHasSuffix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldKey, v))
+}
+
+// KeyEqualFold applies the EqualFold predicate on the "key" field.
+func KeyEqualFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldKey, v))
+}
+
+// KeyContainsFold applies the ContainsFold predicate on the "key" field.
+func KeyContainsFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldKey, v))
+}
+
+// NameEQ applies the EQ predicate on the "name" field.
+func NameEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldName, v))
+}
+
+// NameNEQ applies the NEQ predicate on the "name" field.
+func NameNEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldName, v))
+}
+
+// NameIn applies the In predicate on the "name" field.
+func NameIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldName, vs...))
+}
+
+// NameNotIn applies the NotIn predicate on the "name" field.
+func NameNotIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldName, vs...))
+}
+
+// NameGT applies the GT predicate on the "name" field.
+func NameGT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldName, v))
+}
+
+// NameGTE applies the GTE predicate on the "name" field.
+func NameGTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldName, v))
+}
+
+// NameLT applies the LT predicate on the "name" field.
+func NameLT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldName, v))
+}
+
+// NameLTE applies the LTE predicate on the "name" field.
+func NameLTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldName, v))
+}
+
+// NameContains applies the Contains predicate on the "name" field.
+func NameContains(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContains(FieldName, v))
+}
+
+// NameHasPrefix applies the HasPrefix predicate on the "name" field.
+func NameHasPrefix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldName, v))
+}
+
+// NameHasSuffix applies the HasSuffix predicate on the "name" field.
+func NameHasSuffix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldName, v))
+}
+
+// NameEqualFold applies the EqualFold predicate on the "name" field.
+func NameEqualFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldName, v))
+}
+
+// NameContainsFold applies the ContainsFold predicate on the "name" field.
+func NameContainsFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldName, v))
+}
+
+// DescriptionEQ applies the EQ predicate on the "description" field.
+func DescriptionEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDescription, v))
+}
+
+// DescriptionNEQ applies the NEQ predicate on the "description" field.
+func DescriptionNEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDescription, v))
+}
+
+// DescriptionIn applies the In predicate on the "description" field.
+func DescriptionIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldDescription, vs...))
+}
+
+// DescriptionNotIn applies the NotIn predicate on the "description" field.
+func DescriptionNotIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDescription, vs...))
+}
+
+// DescriptionGT applies the GT predicate on the "description" field.
+func DescriptionGT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldDescription, v))
+}
+
+// DescriptionGTE applies the GTE predicate on the "description" field.
+func DescriptionGTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDescription, v))
+}
+
+// DescriptionLT applies the LT predicate on the "description" field.
+func DescriptionLT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldDescription, v))
+}
+
+// DescriptionLTE applies the LTE predicate on the "description" field.
+func DescriptionLTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDescription, v))
+}
+
+// DescriptionContains applies the Contains predicate on the "description" field.
+func DescriptionContains(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContains(FieldDescription, v))
+}
+
+// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field.
+func DescriptionHasPrefix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldDescription, v))
+}
+
+// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field.
+func DescriptionHasSuffix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldDescription, v))
+}
+
+// DescriptionEqualFold applies the EqualFold predicate on the "description" field.
+func DescriptionEqualFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldDescription, v))
+}
+
+// DescriptionContainsFold applies the ContainsFold predicate on the "description" field.
+func DescriptionContainsFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldDescription, v))
+}
+
+// TypeEQ applies the EQ predicate on the "type" field.
+func TypeEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldType, v))
+}
+
+// TypeNEQ applies the NEQ predicate on the "type" field.
+func TypeNEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldType, v))
+}
+
+// TypeIn applies the In predicate on the "type" field.
+func TypeIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldType, vs...))
+}
+
+// TypeNotIn applies the NotIn predicate on the "type" field.
+func TypeNotIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldType, vs...))
+}
+
+// TypeGT applies the GT predicate on the "type" field.
+func TypeGT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldType, v))
+}
+
+// TypeGTE applies the GTE predicate on the "type" field.
+func TypeGTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldType, v))
+}
+
+// TypeLT applies the LT predicate on the "type" field.
+func TypeLT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldType, v))
+}
+
+// TypeLTE applies the LTE predicate on the "type" field.
+func TypeLTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldType, v))
+}
+
+// TypeContains applies the Contains predicate on the "type" field.
+func TypeContains(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContains(FieldType, v))
+}
+
+// TypeHasPrefix applies the HasPrefix predicate on the "type" field.
+func TypeHasPrefix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldType, v))
+}
+
+// TypeHasSuffix applies the HasSuffix predicate on the "type" field.
+func TypeHasSuffix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldType, v))
+}
+
+// TypeEqualFold applies the EqualFold predicate on the "type" field.
+func TypeEqualFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldType, v))
+}
+
+// TypeContainsFold applies the ContainsFold predicate on the "type" field.
+func TypeContainsFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldType, v))
+}
+
+// RequiredEQ applies the EQ predicate on the "required" field.
+func RequiredEQ(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldRequired, v))
+}
+
+// RequiredNEQ applies the NEQ predicate on the "required" field.
+func RequiredNEQ(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldRequired, v))
+}
+
+// PlaceholderEQ applies the EQ predicate on the "placeholder" field.
+func PlaceholderEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldPlaceholder, v))
+}
+
+// PlaceholderNEQ applies the NEQ predicate on the "placeholder" field.
+func PlaceholderNEQ(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldPlaceholder, v))
+}
+
+// PlaceholderIn applies the In predicate on the "placeholder" field.
+func PlaceholderIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldPlaceholder, vs...))
+}
+
+// PlaceholderNotIn applies the NotIn predicate on the "placeholder" field.
+func PlaceholderNotIn(vs ...string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldPlaceholder, vs...))
+}
+
+// PlaceholderGT applies the GT predicate on the "placeholder" field.
+func PlaceholderGT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldPlaceholder, v))
+}
+
+// PlaceholderGTE applies the GTE predicate on the "placeholder" field.
+func PlaceholderGTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldPlaceholder, v))
+}
+
+// PlaceholderLT applies the LT predicate on the "placeholder" field.
+func PlaceholderLT(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldPlaceholder, v))
+}
+
+// PlaceholderLTE applies the LTE predicate on the "placeholder" field.
+func PlaceholderLTE(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldPlaceholder, v))
+}
+
+// PlaceholderContains applies the Contains predicate on the "placeholder" field.
+func PlaceholderContains(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContains(FieldPlaceholder, v))
+}
+
+// PlaceholderHasPrefix applies the HasPrefix predicate on the "placeholder" field.
+func PlaceholderHasPrefix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasPrefix(FieldPlaceholder, v))
+}
+
+// PlaceholderHasSuffix applies the HasSuffix predicate on the "placeholder" field.
+func PlaceholderHasSuffix(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldHasSuffix(FieldPlaceholder, v))
+}
+
+// PlaceholderEqualFold applies the EqualFold predicate on the "placeholder" field.
+func PlaceholderEqualFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEqualFold(FieldPlaceholder, v))
+}
+
+// PlaceholderContainsFold applies the ContainsFold predicate on the "placeholder" field.
+func PlaceholderContainsFold(v string) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldContainsFold(FieldPlaceholder, v))
+}
+
+// DisplayOrderEQ applies the EQ predicate on the "display_order" field.
+func DisplayOrderEQ(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldDisplayOrder, v))
+}
+
+// DisplayOrderNEQ applies the NEQ predicate on the "display_order" field.
+func DisplayOrderNEQ(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldDisplayOrder, v))
+}
+
+// DisplayOrderIn applies the In predicate on the "display_order" field.
+func DisplayOrderIn(vs ...int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldIn(FieldDisplayOrder, vs...))
+}
+
+// DisplayOrderNotIn applies the NotIn predicate on the "display_order" field.
+func DisplayOrderNotIn(vs ...int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNotIn(FieldDisplayOrder, vs...))
+}
+
+// DisplayOrderGT applies the GT predicate on the "display_order" field.
+func DisplayOrderGT(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGT(FieldDisplayOrder, v))
+}
+
+// DisplayOrderGTE applies the GTE predicate on the "display_order" field.
+func DisplayOrderGTE(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldGTE(FieldDisplayOrder, v))
+}
+
+// DisplayOrderLT applies the LT predicate on the "display_order" field.
+func DisplayOrderLT(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLT(FieldDisplayOrder, v))
+}
+
+// DisplayOrderLTE applies the LTE predicate on the "display_order" field.
+func DisplayOrderLTE(v int) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldLTE(FieldDisplayOrder, v))
+}
+
+// EnabledEQ applies the EQ predicate on the "enabled" field.
+func EnabledEQ(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldEQ(FieldEnabled, v))
+}
+
+// EnabledNEQ applies the NEQ predicate on the "enabled" field.
+func EnabledNEQ(v bool) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.FieldNEQ(FieldEnabled, v))
+}
+
+// HasValues applies the HasEdge predicate on the "values" edge.
+func HasValues() predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(func(s *sql.Selector) {
+ step := sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, ValuesTable, ValuesColumn),
+ )
+ sqlgraph.HasNeighbors(s, step)
+ })
+}
+
+// HasValuesWith applies the HasEdge predicate on the "values" edge with a given conditions (other predicates).
+func HasValuesWith(preds ...predicate.UserAttributeValue) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(func(s *sql.Selector) {
+ step := newValuesStep()
+ sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+ for _, p := range preds {
+ p(s)
+ }
+ })
+ })
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.UserAttributeDefinition) predicate.UserAttributeDefinition {
+ return predicate.UserAttributeDefinition(sql.NotPredicates(p))
+}
diff --git a/backend/ent/userattributedefinition_create.go b/backend/ent/userattributedefinition_create.go
new file mode 100644
index 00000000..a018c060
--- /dev/null
+++ b/backend/ent/userattributedefinition_create.go
@@ -0,0 +1,1267 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeDefinitionCreate is the builder for creating a UserAttributeDefinition entity.
+type UserAttributeDefinitionCreate struct {
+ config
+ mutation *UserAttributeDefinitionMutation
+ hooks []Hook
+ conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (_c *UserAttributeDefinitionCreate) SetCreatedAt(v time.Time) *UserAttributeDefinitionCreate {
+ _c.mutation.SetCreatedAt(v)
+ return _c
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableCreatedAt(v *time.Time) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetCreatedAt(*v)
+ }
+ return _c
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_c *UserAttributeDefinitionCreate) SetUpdatedAt(v time.Time) *UserAttributeDefinitionCreate {
+ _c.mutation.SetUpdatedAt(v)
+ return _c
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableUpdatedAt(v *time.Time) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetUpdatedAt(*v)
+ }
+ return _c
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (_c *UserAttributeDefinitionCreate) SetDeletedAt(v time.Time) *UserAttributeDefinitionCreate {
+ _c.mutation.SetDeletedAt(v)
+ return _c
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableDeletedAt(v *time.Time) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetDeletedAt(*v)
+ }
+ return _c
+}
+
+// SetKey sets the "key" field.
+func (_c *UserAttributeDefinitionCreate) SetKey(v string) *UserAttributeDefinitionCreate {
+ _c.mutation.SetKey(v)
+ return _c
+}
+
+// SetName sets the "name" field.
+func (_c *UserAttributeDefinitionCreate) SetName(v string) *UserAttributeDefinitionCreate {
+ _c.mutation.SetName(v)
+ return _c
+}
+
+// SetDescription sets the "description" field.
+func (_c *UserAttributeDefinitionCreate) SetDescription(v string) *UserAttributeDefinitionCreate {
+ _c.mutation.SetDescription(v)
+ return _c
+}
+
+// SetNillableDescription sets the "description" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableDescription(v *string) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetDescription(*v)
+ }
+ return _c
+}
+
+// SetType sets the "type" field.
+func (_c *UserAttributeDefinitionCreate) SetType(v string) *UserAttributeDefinitionCreate {
+ _c.mutation.SetType(v)
+ return _c
+}
+
+// SetOptions sets the "options" field.
+func (_c *UserAttributeDefinitionCreate) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionCreate {
+ _c.mutation.SetOptions(v)
+ return _c
+}
+
+// SetRequired sets the "required" field.
+func (_c *UserAttributeDefinitionCreate) SetRequired(v bool) *UserAttributeDefinitionCreate {
+ _c.mutation.SetRequired(v)
+ return _c
+}
+
+// SetNillableRequired sets the "required" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableRequired(v *bool) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetRequired(*v)
+ }
+ return _c
+}
+
+// SetValidation sets the "validation" field.
+func (_c *UserAttributeDefinitionCreate) SetValidation(v map[string]interface{}) *UserAttributeDefinitionCreate {
+ _c.mutation.SetValidation(v)
+ return _c
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (_c *UserAttributeDefinitionCreate) SetPlaceholder(v string) *UserAttributeDefinitionCreate {
+ _c.mutation.SetPlaceholder(v)
+ return _c
+}
+
+// SetNillablePlaceholder sets the "placeholder" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillablePlaceholder(v *string) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetPlaceholder(*v)
+ }
+ return _c
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (_c *UserAttributeDefinitionCreate) SetDisplayOrder(v int) *UserAttributeDefinitionCreate {
+ _c.mutation.SetDisplayOrder(v)
+ return _c
+}
+
+// SetNillableDisplayOrder sets the "display_order" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableDisplayOrder(v *int) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetDisplayOrder(*v)
+ }
+ return _c
+}
+
+// SetEnabled sets the "enabled" field.
+func (_c *UserAttributeDefinitionCreate) SetEnabled(v bool) *UserAttributeDefinitionCreate {
+ _c.mutation.SetEnabled(v)
+ return _c
+}
+
+// SetNillableEnabled sets the "enabled" field if the given value is not nil.
+func (_c *UserAttributeDefinitionCreate) SetNillableEnabled(v *bool) *UserAttributeDefinitionCreate {
+ if v != nil {
+ _c.SetEnabled(*v)
+ }
+ return _c
+}
+
+// AddValueIDs adds the "values" edge to the UserAttributeValue entity by IDs.
+func (_c *UserAttributeDefinitionCreate) AddValueIDs(ids ...int64) *UserAttributeDefinitionCreate {
+ _c.mutation.AddValueIDs(ids...)
+ return _c
+}
+
+// AddValues adds the "values" edges to the UserAttributeValue entity.
+func (_c *UserAttributeDefinitionCreate) AddValues(v ...*UserAttributeValue) *UserAttributeDefinitionCreate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _c.AddValueIDs(ids...)
+}
+
+// Mutation returns the UserAttributeDefinitionMutation object of the builder.
+func (_c *UserAttributeDefinitionCreate) Mutation() *UserAttributeDefinitionMutation {
+ return _c.mutation
+}
+
+// Save creates the UserAttributeDefinition in the database.
+func (_c *UserAttributeDefinitionCreate) Save(ctx context.Context) (*UserAttributeDefinition, error) {
+ if err := _c.defaults(); err != nil {
+ return nil, err
+ }
+ return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (_c *UserAttributeDefinitionCreate) SaveX(ctx context.Context) *UserAttributeDefinition {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserAttributeDefinitionCreate) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserAttributeDefinitionCreate) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_c *UserAttributeDefinitionCreate) defaults() error {
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ if userattributedefinition.DefaultCreatedAt == nil {
+ return fmt.Errorf("ent: uninitialized userattributedefinition.DefaultCreatedAt (forgotten import ent/runtime?)")
+ }
+ v := userattributedefinition.DefaultCreatedAt()
+ _c.mutation.SetCreatedAt(v)
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ if userattributedefinition.DefaultUpdatedAt == nil {
+ return fmt.Errorf("ent: uninitialized userattributedefinition.DefaultUpdatedAt (forgotten import ent/runtime?)")
+ }
+ v := userattributedefinition.DefaultUpdatedAt()
+ _c.mutation.SetUpdatedAt(v)
+ }
+ if _, ok := _c.mutation.Description(); !ok {
+ v := userattributedefinition.DefaultDescription
+ _c.mutation.SetDescription(v)
+ }
+ if _, ok := _c.mutation.Options(); !ok {
+ v := userattributedefinition.DefaultOptions
+ _c.mutation.SetOptions(v)
+ }
+ if _, ok := _c.mutation.Required(); !ok {
+ v := userattributedefinition.DefaultRequired
+ _c.mutation.SetRequired(v)
+ }
+ if _, ok := _c.mutation.Validation(); !ok {
+ v := userattributedefinition.DefaultValidation
+ _c.mutation.SetValidation(v)
+ }
+ if _, ok := _c.mutation.Placeholder(); !ok {
+ v := userattributedefinition.DefaultPlaceholder
+ _c.mutation.SetPlaceholder(v)
+ }
+ if _, ok := _c.mutation.DisplayOrder(); !ok {
+ v := userattributedefinition.DefaultDisplayOrder
+ _c.mutation.SetDisplayOrder(v)
+ }
+ if _, ok := _c.mutation.Enabled(); !ok {
+ v := userattributedefinition.DefaultEnabled
+ _c.mutation.SetEnabled(v)
+ }
+ return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_c *UserAttributeDefinitionCreate) check() error {
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UserAttributeDefinition.created_at"`)}
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "UserAttributeDefinition.updated_at"`)}
+ }
+ if _, ok := _c.mutation.Key(); !ok {
+ return &ValidationError{Name: "key", err: errors.New(`ent: missing required field "UserAttributeDefinition.key"`)}
+ }
+ if v, ok := _c.mutation.Key(); ok {
+ if err := userattributedefinition.KeyValidator(v); err != nil {
+ return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.key": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.Name(); !ok {
+ return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "UserAttributeDefinition.name"`)}
+ }
+ if v, ok := _c.mutation.Name(); ok {
+ if err := userattributedefinition.NameValidator(v); err != nil {
+ return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.name": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.Description(); !ok {
+ return &ValidationError{Name: "description", err: errors.New(`ent: missing required field "UserAttributeDefinition.description"`)}
+ }
+ if _, ok := _c.mutation.GetType(); !ok {
+ return &ValidationError{Name: "type", err: errors.New(`ent: missing required field "UserAttributeDefinition.type"`)}
+ }
+ if v, ok := _c.mutation.GetType(); ok {
+ if err := userattributedefinition.TypeValidator(v); err != nil {
+ return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.type": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.Options(); !ok {
+ return &ValidationError{Name: "options", err: errors.New(`ent: missing required field "UserAttributeDefinition.options"`)}
+ }
+ if _, ok := _c.mutation.Required(); !ok {
+ return &ValidationError{Name: "required", err: errors.New(`ent: missing required field "UserAttributeDefinition.required"`)}
+ }
+ if _, ok := _c.mutation.Validation(); !ok {
+ return &ValidationError{Name: "validation", err: errors.New(`ent: missing required field "UserAttributeDefinition.validation"`)}
+ }
+ if _, ok := _c.mutation.Placeholder(); !ok {
+ return &ValidationError{Name: "placeholder", err: errors.New(`ent: missing required field "UserAttributeDefinition.placeholder"`)}
+ }
+ if v, ok := _c.mutation.Placeholder(); ok {
+ if err := userattributedefinition.PlaceholderValidator(v); err != nil {
+ return &ValidationError{Name: "placeholder", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.placeholder": %w`, err)}
+ }
+ }
+ if _, ok := _c.mutation.DisplayOrder(); !ok {
+ return &ValidationError{Name: "display_order", err: errors.New(`ent: missing required field "UserAttributeDefinition.display_order"`)}
+ }
+ if _, ok := _c.mutation.Enabled(); !ok {
+ return &ValidationError{Name: "enabled", err: errors.New(`ent: missing required field "UserAttributeDefinition.enabled"`)}
+ }
+ return nil
+}
+
+func (_c *UserAttributeDefinitionCreate) sqlSave(ctx context.Context) (*UserAttributeDefinition, error) {
+ if err := _c.check(); err != nil {
+ return nil, err
+ }
+ _node, _spec := _c.createSpec()
+ if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ id := _spec.ID.Value.(int64)
+ _node.ID = int64(id)
+ _c.mutation.id = &_node.ID
+ _c.mutation.done = true
+ return _node, nil
+}
+
+func (_c *UserAttributeDefinitionCreate) createSpec() (*UserAttributeDefinition, *sqlgraph.CreateSpec) {
+ var (
+ _node = &UserAttributeDefinition{config: _c.config}
+ _spec = sqlgraph.NewCreateSpec(userattributedefinition.Table, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
+ )
+ _spec.OnConflict = _c.conflict
+ if value, ok := _c.mutation.CreatedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldCreatedAt, field.TypeTime, value)
+ _node.CreatedAt = value
+ }
+ if value, ok := _c.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldUpdatedAt, field.TypeTime, value)
+ _node.UpdatedAt = value
+ }
+ if value, ok := _c.mutation.DeletedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldDeletedAt, field.TypeTime, value)
+ _node.DeletedAt = &value
+ }
+ if value, ok := _c.mutation.Key(); ok {
+ _spec.SetField(userattributedefinition.FieldKey, field.TypeString, value)
+ _node.Key = value
+ }
+ if value, ok := _c.mutation.Name(); ok {
+ _spec.SetField(userattributedefinition.FieldName, field.TypeString, value)
+ _node.Name = value
+ }
+ if value, ok := _c.mutation.Description(); ok {
+ _spec.SetField(userattributedefinition.FieldDescription, field.TypeString, value)
+ _node.Description = value
+ }
+ if value, ok := _c.mutation.GetType(); ok {
+ _spec.SetField(userattributedefinition.FieldType, field.TypeString, value)
+ _node.Type = value
+ }
+ if value, ok := _c.mutation.Options(); ok {
+ _spec.SetField(userattributedefinition.FieldOptions, field.TypeJSON, value)
+ _node.Options = value
+ }
+ if value, ok := _c.mutation.Required(); ok {
+ _spec.SetField(userattributedefinition.FieldRequired, field.TypeBool, value)
+ _node.Required = value
+ }
+ if value, ok := _c.mutation.Validation(); ok {
+ _spec.SetField(userattributedefinition.FieldValidation, field.TypeJSON, value)
+ _node.Validation = value
+ }
+ if value, ok := _c.mutation.Placeholder(); ok {
+ _spec.SetField(userattributedefinition.FieldPlaceholder, field.TypeString, value)
+ _node.Placeholder = value
+ }
+ if value, ok := _c.mutation.DisplayOrder(); ok {
+ _spec.SetField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
+ _node.DisplayOrder = value
+ }
+ if value, ok := _c.mutation.Enabled(); ok {
+ _spec.SetField(userattributedefinition.FieldEnabled, field.TypeBool, value)
+ _node.Enabled = value
+ }
+ if nodes := _c.mutation.ValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges = append(_spec.Edges, edge)
+ }
+ return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.UserAttributeDefinition.Create().
+// SetCreatedAt(v).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.UserAttributeDefinitionUpsert) {
+// SetCreatedAt(v+v).
+// }).
+// Exec(ctx)
+func (_c *UserAttributeDefinitionCreate) OnConflict(opts ...sql.ConflictOption) *UserAttributeDefinitionUpsertOne {
+ _c.conflict = opts
+ return &UserAttributeDefinitionUpsertOne{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *UserAttributeDefinitionCreate) OnConflictColumns(columns ...string) *UserAttributeDefinitionUpsertOne {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &UserAttributeDefinitionUpsertOne{
+ create: _c,
+ }
+}
+
+type (
+ // UserAttributeDefinitionUpsertOne is the builder for "upsert"-ing
+ // one UserAttributeDefinition node.
+ UserAttributeDefinitionUpsertOne struct {
+ create *UserAttributeDefinitionCreate
+ }
+
+ // UserAttributeDefinitionUpsert is the "OnConflict" setter.
+ UserAttributeDefinitionUpsert struct {
+ *sql.UpdateSet
+ }
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeDefinitionUpsert) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldUpdatedAt, v)
+ return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateUpdatedAt() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldUpdatedAt)
+ return u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsert) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldDeletedAt, v)
+ return u
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateDeletedAt() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldDeletedAt)
+ return u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsert) ClearDeletedAt() *UserAttributeDefinitionUpsert {
+ u.SetNull(userattributedefinition.FieldDeletedAt)
+ return u
+}
+
+// SetKey sets the "key" field.
+func (u *UserAttributeDefinitionUpsert) SetKey(v string) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldKey, v)
+ return u
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateKey() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldKey)
+ return u
+}
+
+// SetName sets the "name" field.
+func (u *UserAttributeDefinitionUpsert) SetName(v string) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldName, v)
+ return u
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateName() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldName)
+ return u
+}
+
+// SetDescription sets the "description" field.
+func (u *UserAttributeDefinitionUpsert) SetDescription(v string) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldDescription, v)
+ return u
+}
+
+// UpdateDescription sets the "description" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateDescription() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldDescription)
+ return u
+}
+
+// SetType sets the "type" field.
+func (u *UserAttributeDefinitionUpsert) SetType(v string) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldType, v)
+ return u
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateType() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldType)
+ return u
+}
+
+// SetOptions sets the "options" field.
+func (u *UserAttributeDefinitionUpsert) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldOptions, v)
+ return u
+}
+
+// UpdateOptions sets the "options" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateOptions() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldOptions)
+ return u
+}
+
+// SetRequired sets the "required" field.
+func (u *UserAttributeDefinitionUpsert) SetRequired(v bool) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldRequired, v)
+ return u
+}
+
+// UpdateRequired sets the "required" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateRequired() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldRequired)
+ return u
+}
+
+// SetValidation sets the "validation" field.
+func (u *UserAttributeDefinitionUpsert) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldValidation, v)
+ return u
+}
+
+// UpdateValidation sets the "validation" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateValidation() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldValidation)
+ return u
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (u *UserAttributeDefinitionUpsert) SetPlaceholder(v string) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldPlaceholder, v)
+ return u
+}
+
+// UpdatePlaceholder sets the "placeholder" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdatePlaceholder() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldPlaceholder)
+ return u
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (u *UserAttributeDefinitionUpsert) SetDisplayOrder(v int) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldDisplayOrder, v)
+ return u
+}
+
+// UpdateDisplayOrder sets the "display_order" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateDisplayOrder() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldDisplayOrder)
+ return u
+}
+
+// AddDisplayOrder adds v to the "display_order" field.
+func (u *UserAttributeDefinitionUpsert) AddDisplayOrder(v int) *UserAttributeDefinitionUpsert {
+ u.Add(userattributedefinition.FieldDisplayOrder, v)
+ return u
+}
+
+// SetEnabled sets the "enabled" field.
+func (u *UserAttributeDefinitionUpsert) SetEnabled(v bool) *UserAttributeDefinitionUpsert {
+ u.Set(userattributedefinition.FieldEnabled, v)
+ return u
+}
+
+// UpdateEnabled sets the "enabled" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsert) UpdateEnabled() *UserAttributeDefinitionUpsert {
+ u.SetExcluded(userattributedefinition.FieldEnabled)
+ return u
+}
+
+// UpdateNewValues updates the mutable fields using the new values that were set on create.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *UserAttributeDefinitionUpsertOne) UpdateNewValues() *UserAttributeDefinitionUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ if _, exists := u.create.mutation.CreatedAt(); exists {
+ s.SetIgnore(userattributedefinition.FieldCreatedAt)
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *UserAttributeDefinitionUpsertOne) Ignore() *UserAttributeDefinitionUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *UserAttributeDefinitionUpsertOne) DoNothing() *UserAttributeDefinitionUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the UserAttributeDefinitionCreate.OnConflict
+// documentation for more info.
+func (u *UserAttributeDefinitionUpsertOne) Update(set func(*UserAttributeDefinitionUpsert)) *UserAttributeDefinitionUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&UserAttributeDefinitionUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeDefinitionUpsertOne) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateUpdatedAt() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsertOne) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDeletedAt(v)
+ })
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateDeletedAt() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDeletedAt()
+ })
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsertOne) ClearDeletedAt() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.ClearDeletedAt()
+ })
+}
+
+// SetKey sets the "key" field.
+func (u *UserAttributeDefinitionUpsertOne) SetKey(v string) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetKey(v)
+ })
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateKey() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateKey()
+ })
+}
+
+// SetName sets the "name" field.
+func (u *UserAttributeDefinitionUpsertOne) SetName(v string) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetName(v)
+ })
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateName() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateName()
+ })
+}
+
+// SetDescription sets the "description" field.
+func (u *UserAttributeDefinitionUpsertOne) SetDescription(v string) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDescription(v)
+ })
+}
+
+// UpdateDescription sets the "description" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateDescription() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDescription()
+ })
+}
+
+// SetType sets the "type" field.
+func (u *UserAttributeDefinitionUpsertOne) SetType(v string) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetType(v)
+ })
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateType() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateType()
+ })
+}
+
+// SetOptions sets the "options" field.
+func (u *UserAttributeDefinitionUpsertOne) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetOptions(v)
+ })
+}
+
+// UpdateOptions sets the "options" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateOptions() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateOptions()
+ })
+}
+
+// SetRequired sets the "required" field.
+func (u *UserAttributeDefinitionUpsertOne) SetRequired(v bool) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetRequired(v)
+ })
+}
+
+// UpdateRequired sets the "required" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateRequired() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateRequired()
+ })
+}
+
+// SetValidation sets the "validation" field.
+func (u *UserAttributeDefinitionUpsertOne) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetValidation(v)
+ })
+}
+
+// UpdateValidation sets the "validation" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateValidation() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateValidation()
+ })
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (u *UserAttributeDefinitionUpsertOne) SetPlaceholder(v string) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetPlaceholder(v)
+ })
+}
+
+// UpdatePlaceholder sets the "placeholder" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdatePlaceholder() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdatePlaceholder()
+ })
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (u *UserAttributeDefinitionUpsertOne) SetDisplayOrder(v int) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDisplayOrder(v)
+ })
+}
+
+// AddDisplayOrder adds v to the "display_order" field.
+func (u *UserAttributeDefinitionUpsertOne) AddDisplayOrder(v int) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.AddDisplayOrder(v)
+ })
+}
+
+// UpdateDisplayOrder sets the "display_order" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateDisplayOrder() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDisplayOrder()
+ })
+}
+
+// SetEnabled sets the "enabled" field.
+func (u *UserAttributeDefinitionUpsertOne) SetEnabled(v bool) *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetEnabled(v)
+ })
+}
+
+// UpdateEnabled sets the "enabled" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertOne) UpdateEnabled() *UserAttributeDefinitionUpsertOne {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateEnabled()
+ })
+}
+
+// Exec executes the query.
+func (u *UserAttributeDefinitionUpsertOne) Exec(ctx context.Context) error {
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for UserAttributeDefinitionCreate.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *UserAttributeDefinitionUpsertOne) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// Exec executes the UPSERT query and returns the inserted/updated ID.
+func (u *UserAttributeDefinitionUpsertOne) ID(ctx context.Context) (id int64, err error) {
+ node, err := u.create.Save(ctx)
+ if err != nil {
+ return id, err
+ }
+ return node.ID, nil
+}
+
+// IDX is like ID, but panics if an error occurs.
+func (u *UserAttributeDefinitionUpsertOne) IDX(ctx context.Context) int64 {
+ id, err := u.ID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// UserAttributeDefinitionCreateBulk is the builder for creating many UserAttributeDefinition entities in bulk.
+type UserAttributeDefinitionCreateBulk struct {
+ config
+ err error
+ builders []*UserAttributeDefinitionCreate
+ conflict []sql.ConflictOption
+}
+
+// Save creates the UserAttributeDefinition entities in the database.
+func (_c *UserAttributeDefinitionCreateBulk) Save(ctx context.Context) ([]*UserAttributeDefinition, error) {
+ if _c.err != nil {
+ return nil, _c.err
+ }
+ specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
+ nodes := make([]*UserAttributeDefinition, len(_c.builders))
+ mutators := make([]Mutator, len(_c.builders))
+ for i := range _c.builders {
+ func(i int, root context.Context) {
+ builder := _c.builders[i]
+ builder.defaults()
+ var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+ mutation, ok := m.(*UserAttributeDefinitionMutation)
+ if !ok {
+ return nil, fmt.Errorf("unexpected mutation type %T", m)
+ }
+ if err := builder.check(); err != nil {
+ return nil, err
+ }
+ builder.mutation = mutation
+ var err error
+ nodes[i], specs[i] = builder.createSpec()
+ if i < len(mutators)-1 {
+ _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
+ } else {
+ spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+ spec.OnConflict = _c.conflict
+ // Invoke the actual operation on the latest mutation in the chain.
+ if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ mutation.id = &nodes[i].ID
+ if specs[i].ID.Value != nil {
+ id := specs[i].ID.Value.(int64)
+ nodes[i].ID = int64(id)
+ }
+ mutation.done = true
+ return nodes[i], nil
+ })
+ for i := len(builder.hooks) - 1; i >= 0; i-- {
+ mut = builder.hooks[i](mut)
+ }
+ mutators[i] = mut
+ }(i, ctx)
+ }
+ if len(mutators) > 0 {
+ if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_c *UserAttributeDefinitionCreateBulk) SaveX(ctx context.Context) []*UserAttributeDefinition {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserAttributeDefinitionCreateBulk) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserAttributeDefinitionCreateBulk) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.UserAttributeDefinition.CreateBulk(builders...).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.UserAttributeDefinitionUpsert) {
+// SetCreatedAt(v+v).
+// }).
+// Exec(ctx)
+func (_c *UserAttributeDefinitionCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserAttributeDefinitionUpsertBulk {
+ _c.conflict = opts
+ return &UserAttributeDefinitionUpsertBulk{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *UserAttributeDefinitionCreateBulk) OnConflictColumns(columns ...string) *UserAttributeDefinitionUpsertBulk {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &UserAttributeDefinitionUpsertBulk{
+ create: _c,
+ }
+}
+
+// UserAttributeDefinitionUpsertBulk is the builder for "upsert"-ing
+// a bulk of UserAttributeDefinition nodes.
+type UserAttributeDefinitionUpsertBulk struct {
+ create *UserAttributeDefinitionCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *UserAttributeDefinitionUpsertBulk) UpdateNewValues() *UserAttributeDefinitionUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ for _, b := range u.create.builders {
+ if _, exists := b.mutation.CreatedAt(); exists {
+ s.SetIgnore(userattributedefinition.FieldCreatedAt)
+ }
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeDefinition.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *UserAttributeDefinitionUpsertBulk) Ignore() *UserAttributeDefinitionUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *UserAttributeDefinitionUpsertBulk) DoNothing() *UserAttributeDefinitionUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the UserAttributeDefinitionCreateBulk.OnConflict
+// documentation for more info.
+func (u *UserAttributeDefinitionUpsertBulk) Update(set func(*UserAttributeDefinitionUpsert)) *UserAttributeDefinitionUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&UserAttributeDefinitionUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateUpdatedAt() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDeletedAt(v)
+ })
+}
+
+// UpdateDeletedAt sets the "deleted_at" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateDeletedAt() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDeletedAt()
+ })
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (u *UserAttributeDefinitionUpsertBulk) ClearDeletedAt() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.ClearDeletedAt()
+ })
+}
+
+// SetKey sets the "key" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetKey(v string) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetKey(v)
+ })
+}
+
+// UpdateKey sets the "key" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateKey() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateKey()
+ })
+}
+
+// SetName sets the "name" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetName(v string) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetName(v)
+ })
+}
+
+// UpdateName sets the "name" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateName() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateName()
+ })
+}
+
+// SetDescription sets the "description" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetDescription(v string) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDescription(v)
+ })
+}
+
+// UpdateDescription sets the "description" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateDescription() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDescription()
+ })
+}
+
+// SetType sets the "type" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetType(v string) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetType(v)
+ })
+}
+
+// UpdateType sets the "type" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateType() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateType()
+ })
+}
+
+// SetOptions sets the "options" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetOptions(v)
+ })
+}
+
+// UpdateOptions sets the "options" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateOptions() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateOptions()
+ })
+}
+
+// SetRequired sets the "required" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetRequired(v bool) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetRequired(v)
+ })
+}
+
+// UpdateRequired sets the "required" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateRequired() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateRequired()
+ })
+}
+
+// SetValidation sets the "validation" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetValidation(v)
+ })
+}
+
+// UpdateValidation sets the "validation" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateValidation() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateValidation()
+ })
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetPlaceholder(v string) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetPlaceholder(v)
+ })
+}
+
+// UpdatePlaceholder sets the "placeholder" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdatePlaceholder() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdatePlaceholder()
+ })
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetDisplayOrder(v int) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetDisplayOrder(v)
+ })
+}
+
+// AddDisplayOrder adds v to the "display_order" field.
+func (u *UserAttributeDefinitionUpsertBulk) AddDisplayOrder(v int) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.AddDisplayOrder(v)
+ })
+}
+
+// UpdateDisplayOrder sets the "display_order" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateDisplayOrder() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateDisplayOrder()
+ })
+}
+
+// SetEnabled sets the "enabled" field.
+func (u *UserAttributeDefinitionUpsertBulk) SetEnabled(v bool) *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.SetEnabled(v)
+ })
+}
+
+// UpdateEnabled sets the "enabled" field to the value that was provided on create.
+func (u *UserAttributeDefinitionUpsertBulk) UpdateEnabled() *UserAttributeDefinitionUpsertBulk {
+ return u.Update(func(s *UserAttributeDefinitionUpsert) {
+ s.UpdateEnabled()
+ })
+}
+
+// Exec executes the query.
+func (u *UserAttributeDefinitionUpsertBulk) Exec(ctx context.Context) error {
+ if u.create.err != nil {
+ return u.create.err
+ }
+ for i, b := range u.create.builders {
+ if len(b.conflict) != 0 {
+ return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserAttributeDefinitionCreateBulk instead", i)
+ }
+ }
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for UserAttributeDefinitionCreateBulk.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *UserAttributeDefinitionUpsertBulk) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/userattributedefinition_delete.go b/backend/ent/userattributedefinition_delete.go
new file mode 100644
index 00000000..8d879eb5
--- /dev/null
+++ b/backend/ent/userattributedefinition_delete.go
@@ -0,0 +1,88 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+)
+
+// UserAttributeDefinitionDelete is the builder for deleting a UserAttributeDefinition entity.
+type UserAttributeDefinitionDelete struct {
+ config
+ hooks []Hook
+ mutation *UserAttributeDefinitionMutation
+}
+
+// Where appends a list predicates to the UserAttributeDefinitionDelete builder.
+func (_d *UserAttributeDefinitionDelete) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionDelete {
+ _d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (_d *UserAttributeDefinitionDelete) Exec(ctx context.Context) (int, error) {
+ return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserAttributeDefinitionDelete) ExecX(ctx context.Context) int {
+ n, err := _d.Exec(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return n
+}
+
+func (_d *UserAttributeDefinitionDelete) sqlExec(ctx context.Context) (int, error) {
+ _spec := sqlgraph.NewDeleteSpec(userattributedefinition.Table, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
+ if ps := _d.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
+ if err != nil && sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ _d.mutation.done = true
+ return affected, err
+}
+
+// UserAttributeDefinitionDeleteOne is the builder for deleting a single UserAttributeDefinition entity.
+type UserAttributeDefinitionDeleteOne struct {
+ _d *UserAttributeDefinitionDelete
+}
+
+// Where appends a list predicates to the UserAttributeDefinitionDelete builder.
+func (_d *UserAttributeDefinitionDeleteOne) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionDeleteOne {
+ _d._d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query.
+func (_d *UserAttributeDefinitionDeleteOne) Exec(ctx context.Context) error {
+ n, err := _d._d.Exec(ctx)
+ switch {
+ case err != nil:
+ return err
+ case n == 0:
+ return &NotFoundError{userattributedefinition.Label}
+ default:
+ return nil
+ }
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserAttributeDefinitionDeleteOne) ExecX(ctx context.Context) {
+ if err := _d.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/userattributedefinition_query.go b/backend/ent/userattributedefinition_query.go
new file mode 100644
index 00000000..9022d306
--- /dev/null
+++ b/backend/ent/userattributedefinition_query.go
@@ -0,0 +1,606 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "database/sql/driver"
+ "fmt"
+ "math"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeDefinitionQuery is the builder for querying UserAttributeDefinition entities.
+type UserAttributeDefinitionQuery struct {
+ config
+ ctx *QueryContext
+ order []userattributedefinition.OrderOption
+ inters []Interceptor
+ predicates []predicate.UserAttributeDefinition
+ withValues *UserAttributeValueQuery
+ // intermediate query (i.e. traversal path).
+ sql *sql.Selector
+ path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the UserAttributeDefinitionQuery builder.
+func (_q *UserAttributeDefinitionQuery) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionQuery {
+ _q.predicates = append(_q.predicates, ps...)
+ return _q
+}
+
+// Limit the number of records to be returned by this query.
+func (_q *UserAttributeDefinitionQuery) Limit(limit int) *UserAttributeDefinitionQuery {
+ _q.ctx.Limit = &limit
+ return _q
+}
+
+// Offset to start from.
+func (_q *UserAttributeDefinitionQuery) Offset(offset int) *UserAttributeDefinitionQuery {
+ _q.ctx.Offset = &offset
+ return _q
+}
+
+// Unique configures the query builder to filter duplicate records on query.
+// By default, unique is set to true, and can be disabled using this method.
+func (_q *UserAttributeDefinitionQuery) Unique(unique bool) *UserAttributeDefinitionQuery {
+ _q.ctx.Unique = &unique
+ return _q
+}
+
+// Order specifies how the records should be ordered.
+func (_q *UserAttributeDefinitionQuery) Order(o ...userattributedefinition.OrderOption) *UserAttributeDefinitionQuery {
+ _q.order = append(_q.order, o...)
+ return _q
+}
+
+// QueryValues chains the current query on the "values" edge.
+func (_q *UserAttributeDefinitionQuery) QueryValues() *UserAttributeValueQuery {
+ query := (&UserAttributeValueClient{config: _q.config}).Query()
+ query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ selector := _q.sqlQuery(ctx)
+ if err := selector.Err(); err != nil {
+ return nil, err
+ }
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributedefinition.Table, userattributedefinition.FieldID, selector),
+ sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, userattributedefinition.ValuesTable, userattributedefinition.ValuesColumn),
+ )
+ fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
+ return fromU, nil
+ }
+ return query
+}
+
+// First returns the first UserAttributeDefinition entity from the query.
+// Returns a *NotFoundError when no UserAttributeDefinition was found.
+func (_q *UserAttributeDefinitionQuery) First(ctx context.Context) (*UserAttributeDefinition, error) {
+ nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
+ if err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nil, &NotFoundError{userattributedefinition.Label}
+ }
+ return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) FirstX(ctx context.Context) *UserAttributeDefinition {
+ node, err := _q.First(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return node
+}
+
+// FirstID returns the first UserAttributeDefinition ID from the query.
+// Returns a *NotFoundError when no UserAttributeDefinition ID was found.
+func (_q *UserAttributeDefinitionQuery) FirstID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ err = &NotFoundError{userattributedefinition.Label}
+ return
+ }
+ return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) FirstIDX(ctx context.Context) int64 {
+ id, err := _q.FirstID(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return id
+}
+
+// Only returns a single UserAttributeDefinition entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one UserAttributeDefinition entity is found.
+// Returns a *NotFoundError when no UserAttributeDefinition entities are found.
+func (_q *UserAttributeDefinitionQuery) Only(ctx context.Context) (*UserAttributeDefinition, error) {
+ nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
+ if err != nil {
+ return nil, err
+ }
+ switch len(nodes) {
+ case 1:
+ return nodes[0], nil
+ case 0:
+ return nil, &NotFoundError{userattributedefinition.Label}
+ default:
+ return nil, &NotSingularError{userattributedefinition.Label}
+ }
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) OnlyX(ctx context.Context) *UserAttributeDefinition {
+ node, err := _q.Only(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// OnlyID is like Only, but returns the only UserAttributeDefinition ID in the query.
+// Returns a *NotSingularError when more than one UserAttributeDefinition ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (_q *UserAttributeDefinitionQuery) OnlyID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
+ return
+ }
+ switch len(ids) {
+ case 1:
+ id = ids[0]
+ case 0:
+ err = &NotFoundError{userattributedefinition.Label}
+ default:
+ err = &NotSingularError{userattributedefinition.Label}
+ }
+ return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) OnlyIDX(ctx context.Context) int64 {
+ id, err := _q.OnlyID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// All executes the query and returns a list of UserAttributeDefinitions.
+func (_q *UserAttributeDefinitionQuery) All(ctx context.Context) ([]*UserAttributeDefinition, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ qr := querierAll[[]*UserAttributeDefinition, *UserAttributeDefinitionQuery]()
+ return withInterceptors[[]*UserAttributeDefinition](ctx, _q, qr, _q.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) AllX(ctx context.Context) []*UserAttributeDefinition {
+ nodes, err := _q.All(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return nodes
+}
+
+// IDs executes the query and returns a list of UserAttributeDefinition IDs.
+func (_q *UserAttributeDefinitionQuery) IDs(ctx context.Context) (ids []int64, err error) {
+ if _q.ctx.Unique == nil && _q.path != nil {
+ _q.Unique(true)
+ }
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
+ if err = _q.Select(userattributedefinition.FieldID).Scan(ctx, &ids); err != nil {
+ return nil, err
+ }
+ return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) IDsX(ctx context.Context) []int64 {
+ ids, err := _q.IDs(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return ids
+}
+
+// Count returns the count of the given query.
+func (_q *UserAttributeDefinitionQuery) Count(ctx context.Context) (int, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return 0, err
+ }
+ return withInterceptors[int](ctx, _q, querierCount[*UserAttributeDefinitionQuery](), _q.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) CountX(ctx context.Context) int {
+ count, err := _q.Count(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (_q *UserAttributeDefinitionQuery) Exist(ctx context.Context) (bool, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
+ switch _, err := _q.FirstID(ctx); {
+ case IsNotFound(err):
+ return false, nil
+ case err != nil:
+ return false, fmt.Errorf("ent: check existence: %w", err)
+ default:
+ return true, nil
+ }
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (_q *UserAttributeDefinitionQuery) ExistX(ctx context.Context) bool {
+ exist, err := _q.Exist(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return exist
+}
+
+// Clone returns a duplicate of the UserAttributeDefinitionQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (_q *UserAttributeDefinitionQuery) Clone() *UserAttributeDefinitionQuery {
+ if _q == nil {
+ return nil
+ }
+ return &UserAttributeDefinitionQuery{
+ config: _q.config,
+ ctx: _q.ctx.Clone(),
+ order: append([]userattributedefinition.OrderOption{}, _q.order...),
+ inters: append([]Interceptor{}, _q.inters...),
+ predicates: append([]predicate.UserAttributeDefinition{}, _q.predicates...),
+ withValues: _q.withValues.Clone(),
+ // clone intermediate query.
+ sql: _q.sql.Clone(),
+ path: _q.path,
+ }
+}
+
+// WithValues tells the query-builder to eager-load the nodes that are connected to
+// the "values" edge. The optional arguments are used to configure the query builder of the edge.
+func (_q *UserAttributeDefinitionQuery) WithValues(opts ...func(*UserAttributeValueQuery)) *UserAttributeDefinitionQuery {
+ query := (&UserAttributeValueClient{config: _q.config}).Query()
+ for _, opt := range opts {
+ opt(query)
+ }
+ _q.withValues = query
+ return _q
+}
+
+// GroupBy is used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+// var v []struct {
+// CreatedAt time.Time `json:"created_at,omitempty"`
+// Count int `json:"count,omitempty"`
+// }
+//
+// client.UserAttributeDefinition.Query().
+// GroupBy(userattributedefinition.FieldCreatedAt).
+// Aggregate(ent.Count()).
+// Scan(ctx, &v)
+func (_q *UserAttributeDefinitionQuery) GroupBy(field string, fields ...string) *UserAttributeDefinitionGroupBy {
+ _q.ctx.Fields = append([]string{field}, fields...)
+ grbuild := &UserAttributeDefinitionGroupBy{build: _q}
+ grbuild.flds = &_q.ctx.Fields
+ grbuild.label = userattributedefinition.Label
+ grbuild.scan = grbuild.Scan
+ return grbuild
+}
+
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
+//
+// Example:
+//
+// var v []struct {
+// CreatedAt time.Time `json:"created_at,omitempty"`
+// }
+//
+// client.UserAttributeDefinition.Query().
+// Select(userattributedefinition.FieldCreatedAt).
+// Scan(ctx, &v)
+func (_q *UserAttributeDefinitionQuery) Select(fields ...string) *UserAttributeDefinitionSelect {
+ _q.ctx.Fields = append(_q.ctx.Fields, fields...)
+ sbuild := &UserAttributeDefinitionSelect{UserAttributeDefinitionQuery: _q}
+ sbuild.label = userattributedefinition.Label
+ sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
+ return sbuild
+}
+
+// Aggregate returns a UserAttributeDefinitionSelect configured with the given aggregations.
+func (_q *UserAttributeDefinitionQuery) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionSelect {
+ return _q.Select().Aggregate(fns...)
+}
+
+func (_q *UserAttributeDefinitionQuery) prepareQuery(ctx context.Context) error {
+ for _, inter := range _q.inters {
+ if inter == nil {
+ return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
+ }
+ if trv, ok := inter.(Traverser); ok {
+ if err := trv.Traverse(ctx, _q); err != nil {
+ return err
+ }
+ }
+ }
+ for _, f := range _q.ctx.Fields {
+ if !userattributedefinition.ValidColumn(f) {
+ return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ }
+ if _q.path != nil {
+ prev, err := _q.path(ctx)
+ if err != nil {
+ return err
+ }
+ _q.sql = prev
+ }
+ return nil
+}
+
+func (_q *UserAttributeDefinitionQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAttributeDefinition, error) {
+ var (
+ nodes = []*UserAttributeDefinition{}
+ _spec = _q.querySpec()
+ loadedTypes = [1]bool{
+ _q.withValues != nil,
+ }
+ )
+ _spec.ScanValues = func(columns []string) ([]any, error) {
+ return (*UserAttributeDefinition).scanValues(nil, columns)
+ }
+ _spec.Assign = func(columns []string, values []any) error {
+ node := &UserAttributeDefinition{config: _q.config}
+ nodes = append(nodes, node)
+ node.Edges.loadedTypes = loadedTypes
+ return node.assignValues(columns, values)
+ }
+ for i := range hooks {
+ hooks[i](ctx, _spec)
+ }
+ if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nodes, nil
+ }
+ if query := _q.withValues; query != nil {
+ if err := _q.loadValues(ctx, query, nodes,
+ func(n *UserAttributeDefinition) { n.Edges.Values = []*UserAttributeValue{} },
+ func(n *UserAttributeDefinition, e *UserAttributeValue) { n.Edges.Values = append(n.Edges.Values, e) }); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+func (_q *UserAttributeDefinitionQuery) loadValues(ctx context.Context, query *UserAttributeValueQuery, nodes []*UserAttributeDefinition, init func(*UserAttributeDefinition), assign func(*UserAttributeDefinition, *UserAttributeValue)) error {
+ fks := make([]driver.Value, 0, len(nodes))
+ nodeids := make(map[int64]*UserAttributeDefinition)
+ for i := range nodes {
+ fks = append(fks, nodes[i].ID)
+ nodeids[nodes[i].ID] = nodes[i]
+ if init != nil {
+ init(nodes[i])
+ }
+ }
+ if len(query.ctx.Fields) > 0 {
+ query.ctx.AppendFieldOnce(userattributevalue.FieldAttributeID)
+ }
+ query.Where(predicate.UserAttributeValue(func(s *sql.Selector) {
+ s.Where(sql.InValues(s.C(userattributedefinition.ValuesColumn), fks...))
+ }))
+ neighbors, err := query.All(ctx)
+ if err != nil {
+ return err
+ }
+ for _, n := range neighbors {
+ fk := n.AttributeID
+ node, ok := nodeids[fk]
+ if !ok {
+ return fmt.Errorf(`unexpected referenced foreign-key "attribute_id" returned %v for node %v`, fk, n.ID)
+ }
+ assign(node, n)
+ }
+ return nil
+}
+
+func (_q *UserAttributeDefinitionQuery) sqlCount(ctx context.Context) (int, error) {
+ _spec := _q.querySpec()
+ _spec.Node.Columns = _q.ctx.Fields
+ if len(_q.ctx.Fields) > 0 {
+ _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
+ }
+ return sqlgraph.CountNodes(ctx, _q.driver, _spec)
+}
+
+func (_q *UserAttributeDefinitionQuery) querySpec() *sqlgraph.QuerySpec {
+ _spec := sqlgraph.NewQuerySpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
+ _spec.From = _q.sql
+ if unique := _q.ctx.Unique; unique != nil {
+ _spec.Unique = *unique
+ } else if _q.path != nil {
+ _spec.Unique = true
+ }
+ if fields := _q.ctx.Fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userattributedefinition.FieldID)
+ for i := range fields {
+ if fields[i] != userattributedefinition.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+ }
+ }
+ }
+ if ps := _q.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ _spec.Limit = *limit
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ _spec.Offset = *offset
+ }
+ if ps := _q.order; len(ps) > 0 {
+ _spec.Order = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ return _spec
+}
+
+func (_q *UserAttributeDefinitionQuery) sqlQuery(ctx context.Context) *sql.Selector {
+ builder := sql.Dialect(_q.driver.Dialect())
+ t1 := builder.Table(userattributedefinition.Table)
+ columns := _q.ctx.Fields
+ if len(columns) == 0 {
+ columns = userattributedefinition.Columns
+ }
+ selector := builder.Select(t1.Columns(columns...)...).From(t1)
+ if _q.sql != nil {
+ selector = _q.sql
+ selector.Select(selector.Columns(columns...)...)
+ }
+ if _q.ctx.Unique != nil && *_q.ctx.Unique {
+ selector.Distinct()
+ }
+ for _, p := range _q.predicates {
+ p(selector)
+ }
+ for _, p := range _q.order {
+ p(selector)
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ // limit is mandatory for offset clause. We start
+ // with default value, and override it below if needed.
+ selector.Offset(*offset).Limit(math.MaxInt32)
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ selector.Limit(*limit)
+ }
+ return selector
+}
+
+// UserAttributeDefinitionGroupBy is the group-by builder for UserAttributeDefinition entities.
+type UserAttributeDefinitionGroupBy struct {
+ selector
+ build *UserAttributeDefinitionQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (_g *UserAttributeDefinitionGroupBy) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionGroupBy {
+ _g.fns = append(_g.fns, fns...)
+ return _g
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_g *UserAttributeDefinitionGroupBy) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
+ if err := _g.build.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserAttributeDefinitionQuery, *UserAttributeDefinitionGroupBy](ctx, _g.build, _g, _g.build.inters, v)
+}
+
+func (_g *UserAttributeDefinitionGroupBy) sqlScan(ctx context.Context, root *UserAttributeDefinitionQuery, v any) error {
+ selector := root.sqlQuery(ctx).Select()
+ aggregation := make([]string, 0, len(_g.fns))
+ for _, fn := range _g.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ if len(selector.SelectedColumns()) == 0 {
+ columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
+ for _, f := range *_g.flds {
+ columns = append(columns, selector.C(f))
+ }
+ columns = append(columns, aggregation...)
+ selector.Select(columns...)
+ }
+ selector.GroupBy(selector.Columns(*_g.flds...)...)
+ if err := selector.Err(); err != nil {
+ return err
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
+
+// UserAttributeDefinitionSelect is the builder for selecting fields of UserAttributeDefinition entities.
+type UserAttributeDefinitionSelect struct {
+ *UserAttributeDefinitionQuery
+ selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (_s *UserAttributeDefinitionSelect) Aggregate(fns ...AggregateFunc) *UserAttributeDefinitionSelect {
+ _s.fns = append(_s.fns, fns...)
+ return _s
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_s *UserAttributeDefinitionSelect) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
+ if err := _s.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserAttributeDefinitionQuery, *UserAttributeDefinitionSelect](ctx, _s.UserAttributeDefinitionQuery, _s, _s.inters, v)
+}
+
+func (_s *UserAttributeDefinitionSelect) sqlScan(ctx context.Context, root *UserAttributeDefinitionQuery, v any) error {
+ selector := root.sqlQuery(ctx)
+ aggregation := make([]string, 0, len(_s.fns))
+ for _, fn := range _s.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ switch n := len(*_s.selector.flds); {
+ case n == 0 && len(aggregation) > 0:
+ selector.Select(aggregation...)
+ case n != 0 && len(aggregation) > 0:
+ selector.AppendSelect(aggregation...)
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _s.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
diff --git a/backend/ent/userattributedefinition_update.go b/backend/ent/userattributedefinition_update.go
new file mode 100644
index 00000000..6b9eb7d0
--- /dev/null
+++ b/backend/ent/userattributedefinition_update.go
@@ -0,0 +1,846 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/dialect/sql/sqljson"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeDefinitionUpdate is the builder for updating UserAttributeDefinition entities.
+type UserAttributeDefinitionUpdate struct {
+ config
+ hooks []Hook
+ mutation *UserAttributeDefinitionMutation
+}
+
+// Where appends a list predicates to the UserAttributeDefinitionUpdate builder.
+func (_u *UserAttributeDefinitionUpdate) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionUpdate {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *UserAttributeDefinitionUpdate) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (_u *UserAttributeDefinitionUpdate) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetDeletedAt(v)
+ return _u
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableDeletedAt(v *time.Time) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetDeletedAt(*v)
+ }
+ return _u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (_u *UserAttributeDefinitionUpdate) ClearDeletedAt() *UserAttributeDefinitionUpdate {
+ _u.mutation.ClearDeletedAt()
+ return _u
+}
+
+// SetKey sets the "key" field.
+func (_u *UserAttributeDefinitionUpdate) SetKey(v string) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetKey(v)
+ return _u
+}
+
+// SetNillableKey sets the "key" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableKey(v *string) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetKey(*v)
+ }
+ return _u
+}
+
+// SetName sets the "name" field.
+func (_u *UserAttributeDefinitionUpdate) SetName(v string) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetName(v)
+ return _u
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableName(v *string) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetName(*v)
+ }
+ return _u
+}
+
+// SetDescription sets the "description" field.
+func (_u *UserAttributeDefinitionUpdate) SetDescription(v string) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetDescription(v)
+ return _u
+}
+
+// SetNillableDescription sets the "description" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableDescription(v *string) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetDescription(*v)
+ }
+ return _u
+}
+
+// SetType sets the "type" field.
+func (_u *UserAttributeDefinitionUpdate) SetType(v string) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetType(v)
+ return _u
+}
+
+// SetNillableType sets the "type" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableType(v *string) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetType(*v)
+ }
+ return _u
+}
+
+// SetOptions sets the "options" field.
+func (_u *UserAttributeDefinitionUpdate) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetOptions(v)
+ return _u
+}
+
+// AppendOptions appends value to the "options" field.
+func (_u *UserAttributeDefinitionUpdate) AppendOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdate {
+ _u.mutation.AppendOptions(v)
+ return _u
+}
+
+// SetRequired sets the "required" field.
+func (_u *UserAttributeDefinitionUpdate) SetRequired(v bool) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetRequired(v)
+ return _u
+}
+
+// SetNillableRequired sets the "required" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableRequired(v *bool) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetRequired(*v)
+ }
+ return _u
+}
+
+// SetValidation sets the "validation" field.
+func (_u *UserAttributeDefinitionUpdate) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetValidation(v)
+ return _u
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (_u *UserAttributeDefinitionUpdate) SetPlaceholder(v string) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetPlaceholder(v)
+ return _u
+}
+
+// SetNillablePlaceholder sets the "placeholder" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillablePlaceholder(v *string) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetPlaceholder(*v)
+ }
+ return _u
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (_u *UserAttributeDefinitionUpdate) SetDisplayOrder(v int) *UserAttributeDefinitionUpdate {
+ _u.mutation.ResetDisplayOrder()
+ _u.mutation.SetDisplayOrder(v)
+ return _u
+}
+
+// SetNillableDisplayOrder sets the "display_order" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableDisplayOrder(v *int) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetDisplayOrder(*v)
+ }
+ return _u
+}
+
+// AddDisplayOrder adds value to the "display_order" field.
+func (_u *UserAttributeDefinitionUpdate) AddDisplayOrder(v int) *UserAttributeDefinitionUpdate {
+ _u.mutation.AddDisplayOrder(v)
+ return _u
+}
+
+// SetEnabled sets the "enabled" field.
+func (_u *UserAttributeDefinitionUpdate) SetEnabled(v bool) *UserAttributeDefinitionUpdate {
+ _u.mutation.SetEnabled(v)
+ return _u
+}
+
+// SetNillableEnabled sets the "enabled" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdate) SetNillableEnabled(v *bool) *UserAttributeDefinitionUpdate {
+ if v != nil {
+ _u.SetEnabled(*v)
+ }
+ return _u
+}
+
+// AddValueIDs adds the "values" edge to the UserAttributeValue entity by IDs.
+func (_u *UserAttributeDefinitionUpdate) AddValueIDs(ids ...int64) *UserAttributeDefinitionUpdate {
+ _u.mutation.AddValueIDs(ids...)
+ return _u
+}
+
+// AddValues adds the "values" edges to the UserAttributeValue entity.
+func (_u *UserAttributeDefinitionUpdate) AddValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.AddValueIDs(ids...)
+}
+
+// Mutation returns the UserAttributeDefinitionMutation object of the builder.
+func (_u *UserAttributeDefinitionUpdate) Mutation() *UserAttributeDefinitionMutation {
+ return _u.mutation
+}
+
+// ClearValues clears all "values" edges to the UserAttributeValue entity.
+func (_u *UserAttributeDefinitionUpdate) ClearValues() *UserAttributeDefinitionUpdate {
+ _u.mutation.ClearValues()
+ return _u
+}
+
+// RemoveValueIDs removes the "values" edge to UserAttributeValue entities by IDs.
+func (_u *UserAttributeDefinitionUpdate) RemoveValueIDs(ids ...int64) *UserAttributeDefinitionUpdate {
+ _u.mutation.RemoveValueIDs(ids...)
+ return _u
+}
+
+// RemoveValues removes "values" edges to UserAttributeValue entities.
+func (_u *UserAttributeDefinitionUpdate) RemoveValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.RemoveValueIDs(ids...)
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (_u *UserAttributeDefinitionUpdate) Save(ctx context.Context) (int, error) {
+ if err := _u.defaults(); err != nil {
+ return 0, err
+ }
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserAttributeDefinitionUpdate) SaveX(ctx context.Context) int {
+ affected, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return affected
+}
+
+// Exec executes the query.
+func (_u *UserAttributeDefinitionUpdate) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserAttributeDefinitionUpdate) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *UserAttributeDefinitionUpdate) defaults() error {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ if userattributedefinition.UpdateDefaultUpdatedAt == nil {
+ return fmt.Errorf("ent: uninitialized userattributedefinition.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+ }
+ v := userattributedefinition.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+ return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *UserAttributeDefinitionUpdate) check() error {
+ if v, ok := _u.mutation.Key(); ok {
+ if err := userattributedefinition.KeyValidator(v); err != nil {
+ return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.key": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Name(); ok {
+ if err := userattributedefinition.NameValidator(v); err != nil {
+ return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.name": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.GetType(); ok {
+ if err := userattributedefinition.TypeValidator(v); err != nil {
+ return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.type": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Placeholder(); ok {
+ if err := userattributedefinition.PlaceholderValidator(v); err != nil {
+ return &ValidationError{Name: "placeholder", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.placeholder": %w`, err)}
+ }
+ }
+ return nil
+}
+
+func (_u *UserAttributeDefinitionUpdate) sqlSave(ctx context.Context) (_node int, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldUpdatedAt, field.TypeTime, value)
+ }
+ if value, ok := _u.mutation.DeletedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldDeletedAt, field.TypeTime, value)
+ }
+ if _u.mutation.DeletedAtCleared() {
+ _spec.ClearField(userattributedefinition.FieldDeletedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.Key(); ok {
+ _spec.SetField(userattributedefinition.FieldKey, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Name(); ok {
+ _spec.SetField(userattributedefinition.FieldName, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Description(); ok {
+ _spec.SetField(userattributedefinition.FieldDescription, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.GetType(); ok {
+ _spec.SetField(userattributedefinition.FieldType, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Options(); ok {
+ _spec.SetField(userattributedefinition.FieldOptions, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.AppendedOptions(); ok {
+ _spec.AddModifier(func(u *sql.UpdateBuilder) {
+ sqljson.Append(u, userattributedefinition.FieldOptions, value)
+ })
+ }
+ if value, ok := _u.mutation.Required(); ok {
+ _spec.SetField(userattributedefinition.FieldRequired, field.TypeBool, value)
+ }
+ if value, ok := _u.mutation.Validation(); ok {
+ _spec.SetField(userattributedefinition.FieldValidation, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.Placeholder(); ok {
+ _spec.SetField(userattributedefinition.FieldPlaceholder, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.DisplayOrder(); ok {
+ _spec.SetField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedDisplayOrder(); ok {
+ _spec.AddField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.Enabled(); ok {
+ _spec.SetField(userattributedefinition.FieldEnabled, field.TypeBool, value)
+ }
+ if _u.mutation.ValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.RemovedValuesIDs(); len(nodes) > 0 && !_u.mutation.ValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.ValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userattributedefinition.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return 0, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
+
+// UserAttributeDefinitionUpdateOne is the builder for updating a single UserAttributeDefinition entity.
+type UserAttributeDefinitionUpdateOne struct {
+ config
+ fields []string
+ hooks []Hook
+ mutation *UserAttributeDefinitionMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetUpdatedAt(v time.Time) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// SetDeletedAt sets the "deleted_at" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetDeletedAt(v time.Time) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetDeletedAt(v)
+ return _u
+}
+
+// SetNillableDeletedAt sets the "deleted_at" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableDeletedAt(v *time.Time) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetDeletedAt(*v)
+ }
+ return _u
+}
+
+// ClearDeletedAt clears the value of the "deleted_at" field.
+func (_u *UserAttributeDefinitionUpdateOne) ClearDeletedAt() *UserAttributeDefinitionUpdateOne {
+ _u.mutation.ClearDeletedAt()
+ return _u
+}
+
+// SetKey sets the "key" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetKey(v string) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetKey(v)
+ return _u
+}
+
+// SetNillableKey sets the "key" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableKey(v *string) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetKey(*v)
+ }
+ return _u
+}
+
+// SetName sets the "name" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetName(v string) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetName(v)
+ return _u
+}
+
+// SetNillableName sets the "name" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableName(v *string) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetName(*v)
+ }
+ return _u
+}
+
+// SetDescription sets the "description" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetDescription(v string) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetDescription(v)
+ return _u
+}
+
+// SetNillableDescription sets the "description" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableDescription(v *string) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetDescription(*v)
+ }
+ return _u
+}
+
+// SetType sets the "type" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetType(v string) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetType(v)
+ return _u
+}
+
+// SetNillableType sets the "type" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableType(v *string) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetType(*v)
+ }
+ return _u
+}
+
+// SetOptions sets the "options" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetOptions(v)
+ return _u
+}
+
+// AppendOptions appends value to the "options" field.
+func (_u *UserAttributeDefinitionUpdateOne) AppendOptions(v []map[string]interface{}) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.AppendOptions(v)
+ return _u
+}
+
+// SetRequired sets the "required" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetRequired(v bool) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetRequired(v)
+ return _u
+}
+
+// SetNillableRequired sets the "required" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableRequired(v *bool) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetRequired(*v)
+ }
+ return _u
+}
+
+// SetValidation sets the "validation" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetValidation(v map[string]interface{}) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetValidation(v)
+ return _u
+}
+
+// SetPlaceholder sets the "placeholder" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetPlaceholder(v string) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetPlaceholder(v)
+ return _u
+}
+
+// SetNillablePlaceholder sets the "placeholder" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillablePlaceholder(v *string) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetPlaceholder(*v)
+ }
+ return _u
+}
+
+// SetDisplayOrder sets the "display_order" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetDisplayOrder(v int) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.ResetDisplayOrder()
+ _u.mutation.SetDisplayOrder(v)
+ return _u
+}
+
+// SetNillableDisplayOrder sets the "display_order" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableDisplayOrder(v *int) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetDisplayOrder(*v)
+ }
+ return _u
+}
+
+// AddDisplayOrder adds value to the "display_order" field.
+func (_u *UserAttributeDefinitionUpdateOne) AddDisplayOrder(v int) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.AddDisplayOrder(v)
+ return _u
+}
+
+// SetEnabled sets the "enabled" field.
+func (_u *UserAttributeDefinitionUpdateOne) SetEnabled(v bool) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.SetEnabled(v)
+ return _u
+}
+
+// SetNillableEnabled sets the "enabled" field if the given value is not nil.
+func (_u *UserAttributeDefinitionUpdateOne) SetNillableEnabled(v *bool) *UserAttributeDefinitionUpdateOne {
+ if v != nil {
+ _u.SetEnabled(*v)
+ }
+ return _u
+}
+
+// AddValueIDs adds the "values" edge to the UserAttributeValue entity by IDs.
+func (_u *UserAttributeDefinitionUpdateOne) AddValueIDs(ids ...int64) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.AddValueIDs(ids...)
+ return _u
+}
+
+// AddValues adds the "values" edges to the UserAttributeValue entity.
+func (_u *UserAttributeDefinitionUpdateOne) AddValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdateOne {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.AddValueIDs(ids...)
+}
+
+// Mutation returns the UserAttributeDefinitionMutation object of the builder.
+func (_u *UserAttributeDefinitionUpdateOne) Mutation() *UserAttributeDefinitionMutation {
+ return _u.mutation
+}
+
+// ClearValues clears all "values" edges to the UserAttributeValue entity.
+func (_u *UserAttributeDefinitionUpdateOne) ClearValues() *UserAttributeDefinitionUpdateOne {
+ _u.mutation.ClearValues()
+ return _u
+}
+
+// RemoveValueIDs removes the "values" edge to UserAttributeValue entities by IDs.
+func (_u *UserAttributeDefinitionUpdateOne) RemoveValueIDs(ids ...int64) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.RemoveValueIDs(ids...)
+ return _u
+}
+
+// RemoveValues removes "values" edges to UserAttributeValue entities.
+func (_u *UserAttributeDefinitionUpdateOne) RemoveValues(v ...*UserAttributeValue) *UserAttributeDefinitionUpdateOne {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.RemoveValueIDs(ids...)
+}
+
+// Where appends a list predicates to the UserAttributeDefinitionUpdate builder.
+func (_u *UserAttributeDefinitionUpdateOne) Where(ps ...predicate.UserAttributeDefinition) *UserAttributeDefinitionUpdateOne {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (_u *UserAttributeDefinitionUpdateOne) Select(field string, fields ...string) *UserAttributeDefinitionUpdateOne {
+ _u.fields = append([]string{field}, fields...)
+ return _u
+}
+
+// Save executes the query and returns the updated UserAttributeDefinition entity.
+func (_u *UserAttributeDefinitionUpdateOne) Save(ctx context.Context) (*UserAttributeDefinition, error) {
+ if err := _u.defaults(); err != nil {
+ return nil, err
+ }
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserAttributeDefinitionUpdateOne) SaveX(ctx context.Context) *UserAttributeDefinition {
+ node, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// Exec executes the query on the entity.
+func (_u *UserAttributeDefinitionUpdateOne) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserAttributeDefinitionUpdateOne) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *UserAttributeDefinitionUpdateOne) defaults() error {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ if userattributedefinition.UpdateDefaultUpdatedAt == nil {
+ return fmt.Errorf("ent: uninitialized userattributedefinition.UpdateDefaultUpdatedAt (forgotten import ent/runtime?)")
+ }
+ v := userattributedefinition.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+ return nil
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *UserAttributeDefinitionUpdateOne) check() error {
+ if v, ok := _u.mutation.Key(); ok {
+ if err := userattributedefinition.KeyValidator(v); err != nil {
+ return &ValidationError{Name: "key", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.key": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Name(); ok {
+ if err := userattributedefinition.NameValidator(v); err != nil {
+ return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.name": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.GetType(); ok {
+ if err := userattributedefinition.TypeValidator(v); err != nil {
+ return &ValidationError{Name: "type", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.type": %w`, err)}
+ }
+ }
+ if v, ok := _u.mutation.Placeholder(); ok {
+ if err := userattributedefinition.PlaceholderValidator(v); err != nil {
+ return &ValidationError{Name: "placeholder", err: fmt.Errorf(`ent: validator failed for field "UserAttributeDefinition.placeholder": %w`, err)}
+ }
+ }
+ return nil
+}
+
+func (_u *UserAttributeDefinitionUpdateOne) sqlSave(ctx context.Context) (_node *UserAttributeDefinition, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(userattributedefinition.Table, userattributedefinition.Columns, sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64))
+ id, ok := _u.mutation.ID()
+ if !ok {
+ return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAttributeDefinition.id" for update`)}
+ }
+ _spec.Node.ID.Value = id
+ if fields := _u.fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userattributedefinition.FieldID)
+ for _, f := range fields {
+ if !userattributedefinition.ValidColumn(f) {
+ return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ if f != userattributedefinition.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, f)
+ }
+ }
+ }
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldUpdatedAt, field.TypeTime, value)
+ }
+ if value, ok := _u.mutation.DeletedAt(); ok {
+ _spec.SetField(userattributedefinition.FieldDeletedAt, field.TypeTime, value)
+ }
+ if _u.mutation.DeletedAtCleared() {
+ _spec.ClearField(userattributedefinition.FieldDeletedAt, field.TypeTime)
+ }
+ if value, ok := _u.mutation.Key(); ok {
+ _spec.SetField(userattributedefinition.FieldKey, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Name(); ok {
+ _spec.SetField(userattributedefinition.FieldName, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Description(); ok {
+ _spec.SetField(userattributedefinition.FieldDescription, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.GetType(); ok {
+ _spec.SetField(userattributedefinition.FieldType, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.Options(); ok {
+ _spec.SetField(userattributedefinition.FieldOptions, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.AppendedOptions(); ok {
+ _spec.AddModifier(func(u *sql.UpdateBuilder) {
+ sqljson.Append(u, userattributedefinition.FieldOptions, value)
+ })
+ }
+ if value, ok := _u.mutation.Required(); ok {
+ _spec.SetField(userattributedefinition.FieldRequired, field.TypeBool, value)
+ }
+ if value, ok := _u.mutation.Validation(); ok {
+ _spec.SetField(userattributedefinition.FieldValidation, field.TypeJSON, value)
+ }
+ if value, ok := _u.mutation.Placeholder(); ok {
+ _spec.SetField(userattributedefinition.FieldPlaceholder, field.TypeString, value)
+ }
+ if value, ok := _u.mutation.DisplayOrder(); ok {
+ _spec.SetField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.AddedDisplayOrder(); ok {
+ _spec.AddField(userattributedefinition.FieldDisplayOrder, field.TypeInt, value)
+ }
+ if value, ok := _u.mutation.Enabled(); ok {
+ _spec.SetField(userattributedefinition.FieldEnabled, field.TypeBool, value)
+ }
+ if _u.mutation.ValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.RemovedValuesIDs(); len(nodes) > 0 && !_u.mutation.ValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.ValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: userattributedefinition.ValuesTable,
+ Columns: []string{userattributedefinition.ValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ _node = &UserAttributeDefinition{config: _u.config}
+ _spec.Assign = _node.assignValues
+ _spec.ScanValues = _node.scanValues
+ if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userattributedefinition.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
diff --git a/backend/ent/userattributevalue.go b/backend/ent/userattributevalue.go
new file mode 100644
index 00000000..8dced925
--- /dev/null
+++ b/backend/ent/userattributevalue.go
@@ -0,0 +1,198 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "fmt"
+ "strings"
+ "time"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeValue is the model entity for the UserAttributeValue schema.
+type UserAttributeValue struct {
+ config `json:"-"`
+ // ID of the ent.
+ ID int64 `json:"id,omitempty"`
+ // CreatedAt holds the value of the "created_at" field.
+ CreatedAt time.Time `json:"created_at,omitempty"`
+ // UpdatedAt holds the value of the "updated_at" field.
+ UpdatedAt time.Time `json:"updated_at,omitempty"`
+ // UserID holds the value of the "user_id" field.
+ UserID int64 `json:"user_id,omitempty"`
+ // AttributeID holds the value of the "attribute_id" field.
+ AttributeID int64 `json:"attribute_id,omitempty"`
+ // Value holds the value of the "value" field.
+ Value string `json:"value,omitempty"`
+ // Edges holds the relations/edges for other nodes in the graph.
+ // The values are being populated by the UserAttributeValueQuery when eager-loading is set.
+ Edges UserAttributeValueEdges `json:"edges"`
+ selectValues sql.SelectValues
+}
+
+// UserAttributeValueEdges holds the relations/edges for other nodes in the graph.
+type UserAttributeValueEdges struct {
+ // User holds the value of the user edge.
+ User *User `json:"user,omitempty"`
+ // Definition holds the value of the definition edge.
+ Definition *UserAttributeDefinition `json:"definition,omitempty"`
+ // loadedTypes holds the information for reporting if a
+ // type was loaded (or requested) in eager-loading or not.
+ loadedTypes [2]bool
+}
+
+// UserOrErr returns the User value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e UserAttributeValueEdges) UserOrErr() (*User, error) {
+ if e.User != nil {
+ return e.User, nil
+ } else if e.loadedTypes[0] {
+ return nil, &NotFoundError{label: user.Label}
+ }
+ return nil, &NotLoadedError{edge: "user"}
+}
+
+// DefinitionOrErr returns the Definition value or an error if the edge
+// was not loaded in eager-loading, or loaded but was not found.
+func (e UserAttributeValueEdges) DefinitionOrErr() (*UserAttributeDefinition, error) {
+ if e.Definition != nil {
+ return e.Definition, nil
+ } else if e.loadedTypes[1] {
+ return nil, &NotFoundError{label: userattributedefinition.Label}
+ }
+ return nil, &NotLoadedError{edge: "definition"}
+}
+
+// scanValues returns the types for scanning values from sql.Rows.
+func (*UserAttributeValue) scanValues(columns []string) ([]any, error) {
+ values := make([]any, len(columns))
+ for i := range columns {
+ switch columns[i] {
+ case userattributevalue.FieldID, userattributevalue.FieldUserID, userattributevalue.FieldAttributeID:
+ values[i] = new(sql.NullInt64)
+ case userattributevalue.FieldValue:
+ values[i] = new(sql.NullString)
+ case userattributevalue.FieldCreatedAt, userattributevalue.FieldUpdatedAt:
+ values[i] = new(sql.NullTime)
+ default:
+ values[i] = new(sql.UnknownType)
+ }
+ }
+ return values, nil
+}
+
+// assignValues assigns the values that were returned from sql.Rows (after scanning)
+// to the UserAttributeValue fields.
+func (_m *UserAttributeValue) assignValues(columns []string, values []any) error {
+ if m, n := len(values), len(columns); m < n {
+ return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
+ }
+ for i := range columns {
+ switch columns[i] {
+ case userattributevalue.FieldID:
+ value, ok := values[i].(*sql.NullInt64)
+ if !ok {
+ return fmt.Errorf("unexpected type %T for field id", value)
+ }
+ _m.ID = int64(value.Int64)
+ case userattributevalue.FieldCreatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field created_at", values[i])
+ } else if value.Valid {
+ _m.CreatedAt = value.Time
+ }
+ case userattributevalue.FieldUpdatedAt:
+ if value, ok := values[i].(*sql.NullTime); !ok {
+ return fmt.Errorf("unexpected type %T for field updated_at", values[i])
+ } else if value.Valid {
+ _m.UpdatedAt = value.Time
+ }
+ case userattributevalue.FieldUserID:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field user_id", values[i])
+ } else if value.Valid {
+ _m.UserID = value.Int64
+ }
+ case userattributevalue.FieldAttributeID:
+ if value, ok := values[i].(*sql.NullInt64); !ok {
+ return fmt.Errorf("unexpected type %T for field attribute_id", values[i])
+ } else if value.Valid {
+ _m.AttributeID = value.Int64
+ }
+ case userattributevalue.FieldValue:
+ if value, ok := values[i].(*sql.NullString); !ok {
+ return fmt.Errorf("unexpected type %T for field value", values[i])
+ } else if value.Valid {
+ _m.Value = value.String
+ }
+ default:
+ _m.selectValues.Set(columns[i], values[i])
+ }
+ }
+ return nil
+}
+
+// GetValue returns the ent.Value that was dynamically selected and assigned to the UserAttributeValue.
+// This includes values selected through modifiers, order, etc.
+func (_m *UserAttributeValue) GetValue(name string) (ent.Value, error) {
+ return _m.selectValues.Get(name)
+}
+
+// QueryUser queries the "user" edge of the UserAttributeValue entity.
+func (_m *UserAttributeValue) QueryUser() *UserQuery {
+ return NewUserAttributeValueClient(_m.config).QueryUser(_m)
+}
+
+// QueryDefinition queries the "definition" edge of the UserAttributeValue entity.
+func (_m *UserAttributeValue) QueryDefinition() *UserAttributeDefinitionQuery {
+ return NewUserAttributeValueClient(_m.config).QueryDefinition(_m)
+}
+
+// Update returns a builder for updating this UserAttributeValue.
+// Note that you need to call UserAttributeValue.Unwrap() before calling this method if this UserAttributeValue
+// was returned from a transaction, and the transaction was committed or rolled back.
+func (_m *UserAttributeValue) Update() *UserAttributeValueUpdateOne {
+ return NewUserAttributeValueClient(_m.config).UpdateOne(_m)
+}
+
+// Unwrap unwraps the UserAttributeValue entity that was returned from a transaction after it was closed,
+// so that all future queries will be executed through the driver which created the transaction.
+func (_m *UserAttributeValue) Unwrap() *UserAttributeValue {
+ _tx, ok := _m.config.driver.(*txDriver)
+ if !ok {
+ panic("ent: UserAttributeValue is not a transactional entity")
+ }
+ _m.config.driver = _tx.drv
+ return _m
+}
+
+// String implements the fmt.Stringer.
+func (_m *UserAttributeValue) String() string {
+ var builder strings.Builder
+ builder.WriteString("UserAttributeValue(")
+ builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
+ builder.WriteString("created_at=")
+ builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("updated_at=")
+ builder.WriteString(_m.UpdatedAt.Format(time.ANSIC))
+ builder.WriteString(", ")
+ builder.WriteString("user_id=")
+ builder.WriteString(fmt.Sprintf("%v", _m.UserID))
+ builder.WriteString(", ")
+ builder.WriteString("attribute_id=")
+ builder.WriteString(fmt.Sprintf("%v", _m.AttributeID))
+ builder.WriteString(", ")
+ builder.WriteString("value=")
+ builder.WriteString(_m.Value)
+ builder.WriteByte(')')
+ return builder.String()
+}
+
+// UserAttributeValues is a parsable slice of UserAttributeValue.
+type UserAttributeValues []*UserAttributeValue
diff --git a/backend/ent/userattributevalue/userattributevalue.go b/backend/ent/userattributevalue/userattributevalue.go
new file mode 100644
index 00000000..b8bb5842
--- /dev/null
+++ b/backend/ent/userattributevalue/userattributevalue.go
@@ -0,0 +1,139 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userattributevalue
+
+import (
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+)
+
+const (
+ // Label holds the string label denoting the userattributevalue type in the database.
+ Label = "user_attribute_value"
+ // FieldID holds the string denoting the id field in the database.
+ FieldID = "id"
+ // FieldCreatedAt holds the string denoting the created_at field in the database.
+ FieldCreatedAt = "created_at"
+ // FieldUpdatedAt holds the string denoting the updated_at field in the database.
+ FieldUpdatedAt = "updated_at"
+ // FieldUserID holds the string denoting the user_id field in the database.
+ FieldUserID = "user_id"
+ // FieldAttributeID holds the string denoting the attribute_id field in the database.
+ FieldAttributeID = "attribute_id"
+ // FieldValue holds the string denoting the value field in the database.
+ FieldValue = "value"
+ // EdgeUser holds the string denoting the user edge name in mutations.
+ EdgeUser = "user"
+ // EdgeDefinition holds the string denoting the definition edge name in mutations.
+ EdgeDefinition = "definition"
+ // Table holds the table name of the userattributevalue in the database.
+ Table = "user_attribute_values"
+ // UserTable is the table that holds the user relation/edge.
+ UserTable = "user_attribute_values"
+ // UserInverseTable is the table name for the User entity.
+ // It exists in this package in order to avoid circular dependency with the "user" package.
+ UserInverseTable = "users"
+ // UserColumn is the table column denoting the user relation/edge.
+ UserColumn = "user_id"
+ // DefinitionTable is the table that holds the definition relation/edge.
+ DefinitionTable = "user_attribute_values"
+ // DefinitionInverseTable is the table name for the UserAttributeDefinition entity.
+ // It exists in this package in order to avoid circular dependency with the "userattributedefinition" package.
+ DefinitionInverseTable = "user_attribute_definitions"
+ // DefinitionColumn is the table column denoting the definition relation/edge.
+ DefinitionColumn = "attribute_id"
+)
+
+// Columns holds all SQL columns for userattributevalue fields.
+var Columns = []string{
+ FieldID,
+ FieldCreatedAt,
+ FieldUpdatedAt,
+ FieldUserID,
+ FieldAttributeID,
+ FieldValue,
+}
+
+// ValidColumn reports if the column name is valid (part of the table columns).
+func ValidColumn(column string) bool {
+ for i := range Columns {
+ if column == Columns[i] {
+ return true
+ }
+ }
+ return false
+}
+
+var (
+ // DefaultCreatedAt holds the default value on creation for the "created_at" field.
+ DefaultCreatedAt func() time.Time
+ // DefaultUpdatedAt holds the default value on creation for the "updated_at" field.
+ DefaultUpdatedAt func() time.Time
+ // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field.
+ UpdateDefaultUpdatedAt func() time.Time
+ // DefaultValue holds the default value on creation for the "value" field.
+ DefaultValue string
+)
+
+// OrderOption defines the ordering options for the UserAttributeValue queries.
+type OrderOption func(*sql.Selector)
+
+// ByID orders the results by the id field.
+func ByID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldID, opts...).ToFunc()
+}
+
+// ByCreatedAt orders the results by the created_at field.
+func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
+}
+
+// ByUpdatedAt orders the results by the updated_at field.
+func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc()
+}
+
+// ByUserID orders the results by the user_id field.
+func ByUserID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldUserID, opts...).ToFunc()
+}
+
+// ByAttributeID orders the results by the attribute_id field.
+func ByAttributeID(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldAttributeID, opts...).ToFunc()
+}
+
+// ByValue orders the results by the value field.
+func ByValue(opts ...sql.OrderTermOption) OrderOption {
+ return sql.OrderByField(FieldValue, opts...).ToFunc()
+}
+
+// ByUserField orders the results by user field.
+func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
+ }
+}
+
+// ByDefinitionField orders the results by definition field.
+func ByDefinitionField(field string, opts ...sql.OrderTermOption) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborTerms(s, newDefinitionStep(), sql.OrderByField(field, opts...))
+ }
+}
+func newUserStep() *sqlgraph.Step {
+ return sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.To(UserInverseTable, FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
+ )
+}
+func newDefinitionStep() *sqlgraph.Step {
+ return sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.To(DefinitionInverseTable, FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, DefinitionTable, DefinitionColumn),
+ )
+}
diff --git a/backend/ent/userattributevalue/where.go b/backend/ent/userattributevalue/where.go
new file mode 100644
index 00000000..43c3213e
--- /dev/null
+++ b/backend/ent/userattributevalue/where.go
@@ -0,0 +1,327 @@
+// Code generated by ent, DO NOT EDIT.
+
+package userattributevalue
+
+import (
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+)
+
+// ID filters vertices based on their ID field.
+func ID(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldID, id))
+}
+
+// IDEQ applies the EQ predicate on the ID field.
+func IDEQ(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldID, id))
+}
+
+// IDNEQ applies the NEQ predicate on the ID field.
+func IDNEQ(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldID, id))
+}
+
+// IDIn applies the In predicate on the ID field.
+func IDIn(ids ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldID, ids...))
+}
+
+// IDNotIn applies the NotIn predicate on the ID field.
+func IDNotIn(ids ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldID, ids...))
+}
+
+// IDGT applies the GT predicate on the ID field.
+func IDGT(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGT(FieldID, id))
+}
+
+// IDGTE applies the GTE predicate on the ID field.
+func IDGTE(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGTE(FieldID, id))
+}
+
+// IDLT applies the LT predicate on the ID field.
+func IDLT(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLT(FieldID, id))
+}
+
+// IDLTE applies the LTE predicate on the ID field.
+func IDLTE(id int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLTE(FieldID, id))
+}
+
+// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
+func CreatedAt(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ.
+func UpdatedAt(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
+func UserID(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldUserID, v))
+}
+
+// AttributeID applies equality check predicate on the "attribute_id" field. It's identical to AttributeIDEQ.
+func AttributeID(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldAttributeID, v))
+}
+
+// Value applies equality check predicate on the "value" field. It's identical to ValueEQ.
+func Value(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldValue, v))
+}
+
+// CreatedAtEQ applies the EQ predicate on the "created_at" field.
+func CreatedAtEQ(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
+func CreatedAtNEQ(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldCreatedAt, v))
+}
+
+// CreatedAtIn applies the In predicate on the "created_at" field.
+func CreatedAtIn(vs ...time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
+func CreatedAtNotIn(vs ...time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldCreatedAt, vs...))
+}
+
+// CreatedAtGT applies the GT predicate on the "created_at" field.
+func CreatedAtGT(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGT(FieldCreatedAt, v))
+}
+
+// CreatedAtGTE applies the GTE predicate on the "created_at" field.
+func CreatedAtGTE(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGTE(FieldCreatedAt, v))
+}
+
+// CreatedAtLT applies the LT predicate on the "created_at" field.
+func CreatedAtLT(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLT(FieldCreatedAt, v))
+}
+
+// CreatedAtLTE applies the LTE predicate on the "created_at" field.
+func CreatedAtLTE(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLTE(FieldCreatedAt, v))
+}
+
+// UpdatedAtEQ applies the EQ predicate on the "updated_at" field.
+func UpdatedAtEQ(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field.
+func UpdatedAtNEQ(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldUpdatedAt, v))
+}
+
+// UpdatedAtIn applies the In predicate on the "updated_at" field.
+func UpdatedAtIn(vs ...time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field.
+func UpdatedAtNotIn(vs ...time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldUpdatedAt, vs...))
+}
+
+// UpdatedAtGT applies the GT predicate on the "updated_at" field.
+func UpdatedAtGT(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtGTE applies the GTE predicate on the "updated_at" field.
+func UpdatedAtGTE(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGTE(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLT applies the LT predicate on the "updated_at" field.
+func UpdatedAtLT(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLT(FieldUpdatedAt, v))
+}
+
+// UpdatedAtLTE applies the LTE predicate on the "updated_at" field.
+func UpdatedAtLTE(v time.Time) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLTE(FieldUpdatedAt, v))
+}
+
+// UserIDEQ applies the EQ predicate on the "user_id" field.
+func UserIDEQ(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldUserID, v))
+}
+
+// UserIDNEQ applies the NEQ predicate on the "user_id" field.
+func UserIDNEQ(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldUserID, v))
+}
+
+// UserIDIn applies the In predicate on the "user_id" field.
+func UserIDIn(vs ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldUserID, vs...))
+}
+
+// UserIDNotIn applies the NotIn predicate on the "user_id" field.
+func UserIDNotIn(vs ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldUserID, vs...))
+}
+
+// AttributeIDEQ applies the EQ predicate on the "attribute_id" field.
+func AttributeIDEQ(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldAttributeID, v))
+}
+
+// AttributeIDNEQ applies the NEQ predicate on the "attribute_id" field.
+func AttributeIDNEQ(v int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldAttributeID, v))
+}
+
+// AttributeIDIn applies the In predicate on the "attribute_id" field.
+func AttributeIDIn(vs ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldAttributeID, vs...))
+}
+
+// AttributeIDNotIn applies the NotIn predicate on the "attribute_id" field.
+func AttributeIDNotIn(vs ...int64) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldAttributeID, vs...))
+}
+
+// ValueEQ applies the EQ predicate on the "value" field.
+func ValueEQ(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEQ(FieldValue, v))
+}
+
+// ValueNEQ applies the NEQ predicate on the "value" field.
+func ValueNEQ(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNEQ(FieldValue, v))
+}
+
+// ValueIn applies the In predicate on the "value" field.
+func ValueIn(vs ...string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldIn(FieldValue, vs...))
+}
+
+// ValueNotIn applies the NotIn predicate on the "value" field.
+func ValueNotIn(vs ...string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldNotIn(FieldValue, vs...))
+}
+
+// ValueGT applies the GT predicate on the "value" field.
+func ValueGT(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGT(FieldValue, v))
+}
+
+// ValueGTE applies the GTE predicate on the "value" field.
+func ValueGTE(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldGTE(FieldValue, v))
+}
+
+// ValueLT applies the LT predicate on the "value" field.
+func ValueLT(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLT(FieldValue, v))
+}
+
+// ValueLTE applies the LTE predicate on the "value" field.
+func ValueLTE(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldLTE(FieldValue, v))
+}
+
+// ValueContains applies the Contains predicate on the "value" field.
+func ValueContains(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldContains(FieldValue, v))
+}
+
+// ValueHasPrefix applies the HasPrefix predicate on the "value" field.
+func ValueHasPrefix(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldHasPrefix(FieldValue, v))
+}
+
+// ValueHasSuffix applies the HasSuffix predicate on the "value" field.
+func ValueHasSuffix(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldHasSuffix(FieldValue, v))
+}
+
+// ValueEqualFold applies the EqualFold predicate on the "value" field.
+func ValueEqualFold(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldEqualFold(FieldValue, v))
+}
+
+// ValueContainsFold applies the ContainsFold predicate on the "value" field.
+func ValueContainsFold(v string) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.FieldContainsFold(FieldValue, v))
+}
+
+// HasUser applies the HasEdge predicate on the "user" edge.
+func HasUser() predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(func(s *sql.Selector) {
+ step := sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, UserTable, UserColumn),
+ )
+ sqlgraph.HasNeighbors(s, step)
+ })
+}
+
+// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
+func HasUserWith(preds ...predicate.User) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(func(s *sql.Selector) {
+ step := newUserStep()
+ sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+ for _, p := range preds {
+ p(s)
+ }
+ })
+ })
+}
+
+// HasDefinition applies the HasEdge predicate on the "definition" edge.
+func HasDefinition() predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(func(s *sql.Selector) {
+ step := sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, DefinitionTable, DefinitionColumn),
+ )
+ sqlgraph.HasNeighbors(s, step)
+ })
+}
+
+// HasDefinitionWith applies the HasEdge predicate on the "definition" edge with a given conditions (other predicates).
+func HasDefinitionWith(preds ...predicate.UserAttributeDefinition) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(func(s *sql.Selector) {
+ step := newDefinitionStep()
+ sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+ for _, p := range preds {
+ p(s)
+ }
+ })
+ })
+}
+
+// And groups predicates with the AND operator between them.
+func And(predicates ...predicate.UserAttributeValue) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.AndPredicates(predicates...))
+}
+
+// Or groups predicates with the OR operator between them.
+func Or(predicates ...predicate.UserAttributeValue) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.OrPredicates(predicates...))
+}
+
+// Not applies the not operator on the given predicate.
+func Not(p predicate.UserAttributeValue) predicate.UserAttributeValue {
+ return predicate.UserAttributeValue(sql.NotPredicates(p))
+}
diff --git a/backend/ent/userattributevalue_create.go b/backend/ent/userattributevalue_create.go
new file mode 100644
index 00000000..c52481dc
--- /dev/null
+++ b/backend/ent/userattributevalue_create.go
@@ -0,0 +1,731 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeValueCreate is the builder for creating a UserAttributeValue entity.
+type UserAttributeValueCreate struct {
+ config
+ mutation *UserAttributeValueMutation
+ hooks []Hook
+ conflict []sql.ConflictOption
+}
+
+// SetCreatedAt sets the "created_at" field.
+func (_c *UserAttributeValueCreate) SetCreatedAt(v time.Time) *UserAttributeValueCreate {
+ _c.mutation.SetCreatedAt(v)
+ return _c
+}
+
+// SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
+func (_c *UserAttributeValueCreate) SetNillableCreatedAt(v *time.Time) *UserAttributeValueCreate {
+ if v != nil {
+ _c.SetCreatedAt(*v)
+ }
+ return _c
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_c *UserAttributeValueCreate) SetUpdatedAt(v time.Time) *UserAttributeValueCreate {
+ _c.mutation.SetUpdatedAt(v)
+ return _c
+}
+
+// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil.
+func (_c *UserAttributeValueCreate) SetNillableUpdatedAt(v *time.Time) *UserAttributeValueCreate {
+ if v != nil {
+ _c.SetUpdatedAt(*v)
+ }
+ return _c
+}
+
+// SetUserID sets the "user_id" field.
+func (_c *UserAttributeValueCreate) SetUserID(v int64) *UserAttributeValueCreate {
+ _c.mutation.SetUserID(v)
+ return _c
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (_c *UserAttributeValueCreate) SetAttributeID(v int64) *UserAttributeValueCreate {
+ _c.mutation.SetAttributeID(v)
+ return _c
+}
+
+// SetValue sets the "value" field.
+func (_c *UserAttributeValueCreate) SetValue(v string) *UserAttributeValueCreate {
+ _c.mutation.SetValue(v)
+ return _c
+}
+
+// SetNillableValue sets the "value" field if the given value is not nil.
+func (_c *UserAttributeValueCreate) SetNillableValue(v *string) *UserAttributeValueCreate {
+ if v != nil {
+ _c.SetValue(*v)
+ }
+ return _c
+}
+
+// SetUser sets the "user" edge to the User entity.
+func (_c *UserAttributeValueCreate) SetUser(v *User) *UserAttributeValueCreate {
+ return _c.SetUserID(v.ID)
+}
+
+// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
+func (_c *UserAttributeValueCreate) SetDefinitionID(id int64) *UserAttributeValueCreate {
+ _c.mutation.SetDefinitionID(id)
+ return _c
+}
+
+// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
+func (_c *UserAttributeValueCreate) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueCreate {
+ return _c.SetDefinitionID(v.ID)
+}
+
+// Mutation returns the UserAttributeValueMutation object of the builder.
+func (_c *UserAttributeValueCreate) Mutation() *UserAttributeValueMutation {
+ return _c.mutation
+}
+
+// Save creates the UserAttributeValue in the database.
+func (_c *UserAttributeValueCreate) Save(ctx context.Context) (*UserAttributeValue, error) {
+ _c.defaults()
+ return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
+}
+
+// SaveX calls Save and panics if Save returns an error.
+func (_c *UserAttributeValueCreate) SaveX(ctx context.Context) *UserAttributeValue {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserAttributeValueCreate) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserAttributeValueCreate) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_c *UserAttributeValueCreate) defaults() {
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ v := userattributevalue.DefaultCreatedAt()
+ _c.mutation.SetCreatedAt(v)
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ v := userattributevalue.DefaultUpdatedAt()
+ _c.mutation.SetUpdatedAt(v)
+ }
+ if _, ok := _c.mutation.Value(); !ok {
+ v := userattributevalue.DefaultValue
+ _c.mutation.SetValue(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_c *UserAttributeValueCreate) check() error {
+ if _, ok := _c.mutation.CreatedAt(); !ok {
+ return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UserAttributeValue.created_at"`)}
+ }
+ if _, ok := _c.mutation.UpdatedAt(); !ok {
+ return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "UserAttributeValue.updated_at"`)}
+ }
+ if _, ok := _c.mutation.UserID(); !ok {
+ return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "UserAttributeValue.user_id"`)}
+ }
+ if _, ok := _c.mutation.AttributeID(); !ok {
+ return &ValidationError{Name: "attribute_id", err: errors.New(`ent: missing required field "UserAttributeValue.attribute_id"`)}
+ }
+ if _, ok := _c.mutation.Value(); !ok {
+ return &ValidationError{Name: "value", err: errors.New(`ent: missing required field "UserAttributeValue.value"`)}
+ }
+ if len(_c.mutation.UserIDs()) == 0 {
+ return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "UserAttributeValue.user"`)}
+ }
+ if len(_c.mutation.DefinitionIDs()) == 0 {
+ return &ValidationError{Name: "definition", err: errors.New(`ent: missing required edge "UserAttributeValue.definition"`)}
+ }
+ return nil
+}
+
+func (_c *UserAttributeValueCreate) sqlSave(ctx context.Context) (*UserAttributeValue, error) {
+ if err := _c.check(); err != nil {
+ return nil, err
+ }
+ _node, _spec := _c.createSpec()
+ if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ id := _spec.ID.Value.(int64)
+ _node.ID = int64(id)
+ _c.mutation.id = &_node.ID
+ _c.mutation.done = true
+ return _node, nil
+}
+
+func (_c *UserAttributeValueCreate) createSpec() (*UserAttributeValue, *sqlgraph.CreateSpec) {
+ var (
+ _node = &UserAttributeValue{config: _c.config}
+ _spec = sqlgraph.NewCreateSpec(userattributevalue.Table, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
+ )
+ _spec.OnConflict = _c.conflict
+ if value, ok := _c.mutation.CreatedAt(); ok {
+ _spec.SetField(userattributevalue.FieldCreatedAt, field.TypeTime, value)
+ _node.CreatedAt = value
+ }
+ if value, ok := _c.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
+ _node.UpdatedAt = value
+ }
+ if value, ok := _c.mutation.Value(); ok {
+ _spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
+ _node.Value = value
+ }
+ if nodes := _c.mutation.UserIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.UserTable,
+ Columns: []string{userattributevalue.UserColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _node.UserID = nodes[0]
+ _spec.Edges = append(_spec.Edges, edge)
+ }
+ if nodes := _c.mutation.DefinitionIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.DefinitionTable,
+ Columns: []string{userattributevalue.DefinitionColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _node.AttributeID = nodes[0]
+ _spec.Edges = append(_spec.Edges, edge)
+ }
+ return _node, _spec
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.UserAttributeValue.Create().
+// SetCreatedAt(v).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.UserAttributeValueUpsert) {
+// SetCreatedAt(v+v).
+// }).
+// Exec(ctx)
+func (_c *UserAttributeValueCreate) OnConflict(opts ...sql.ConflictOption) *UserAttributeValueUpsertOne {
+ _c.conflict = opts
+ return &UserAttributeValueUpsertOne{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *UserAttributeValueCreate) OnConflictColumns(columns ...string) *UserAttributeValueUpsertOne {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &UserAttributeValueUpsertOne{
+ create: _c,
+ }
+}
+
+type (
+ // UserAttributeValueUpsertOne is the builder for "upsert"-ing
+ // one UserAttributeValue node.
+ UserAttributeValueUpsertOne struct {
+ create *UserAttributeValueCreate
+ }
+
+ // UserAttributeValueUpsert is the "OnConflict" setter.
+ UserAttributeValueUpsert struct {
+ *sql.UpdateSet
+ }
+)
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeValueUpsert) SetUpdatedAt(v time.Time) *UserAttributeValueUpsert {
+ u.Set(userattributevalue.FieldUpdatedAt, v)
+ return u
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeValueUpsert) UpdateUpdatedAt() *UserAttributeValueUpsert {
+ u.SetExcluded(userattributevalue.FieldUpdatedAt)
+ return u
+}
+
+// SetUserID sets the "user_id" field.
+func (u *UserAttributeValueUpsert) SetUserID(v int64) *UserAttributeValueUpsert {
+ u.Set(userattributevalue.FieldUserID, v)
+ return u
+}
+
+// UpdateUserID sets the "user_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsert) UpdateUserID() *UserAttributeValueUpsert {
+ u.SetExcluded(userattributevalue.FieldUserID)
+ return u
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (u *UserAttributeValueUpsert) SetAttributeID(v int64) *UserAttributeValueUpsert {
+ u.Set(userattributevalue.FieldAttributeID, v)
+ return u
+}
+
+// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsert) UpdateAttributeID() *UserAttributeValueUpsert {
+ u.SetExcluded(userattributevalue.FieldAttributeID)
+ return u
+}
+
+// SetValue sets the "value" field.
+func (u *UserAttributeValueUpsert) SetValue(v string) *UserAttributeValueUpsert {
+ u.Set(userattributevalue.FieldValue, v)
+ return u
+}
+
+// UpdateValue sets the "value" field to the value that was provided on create.
+func (u *UserAttributeValueUpsert) UpdateValue() *UserAttributeValueUpsert {
+ u.SetExcluded(userattributevalue.FieldValue)
+ return u
+}
+
+// UpdateNewValues updates the mutable fields using the new values that were set on create.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *UserAttributeValueUpsertOne) UpdateNewValues() *UserAttributeValueUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ if _, exists := u.create.mutation.CreatedAt(); exists {
+ s.SetIgnore(userattributevalue.FieldCreatedAt)
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *UserAttributeValueUpsertOne) Ignore() *UserAttributeValueUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *UserAttributeValueUpsertOne) DoNothing() *UserAttributeValueUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the UserAttributeValueCreate.OnConflict
+// documentation for more info.
+func (u *UserAttributeValueUpsertOne) Update(set func(*UserAttributeValueUpsert)) *UserAttributeValueUpsertOne {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&UserAttributeValueUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeValueUpsertOne) SetUpdatedAt(v time.Time) *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertOne) UpdateUpdatedAt() *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// SetUserID sets the "user_id" field.
+func (u *UserAttributeValueUpsertOne) SetUserID(v int64) *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetUserID(v)
+ })
+}
+
+// UpdateUserID sets the "user_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertOne) UpdateUserID() *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateUserID()
+ })
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (u *UserAttributeValueUpsertOne) SetAttributeID(v int64) *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetAttributeID(v)
+ })
+}
+
+// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertOne) UpdateAttributeID() *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateAttributeID()
+ })
+}
+
+// SetValue sets the "value" field.
+func (u *UserAttributeValueUpsertOne) SetValue(v string) *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetValue(v)
+ })
+}
+
+// UpdateValue sets the "value" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertOne) UpdateValue() *UserAttributeValueUpsertOne {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateValue()
+ })
+}
+
+// Exec executes the query.
+func (u *UserAttributeValueUpsertOne) Exec(ctx context.Context) error {
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for UserAttributeValueCreate.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *UserAttributeValueUpsertOne) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// Exec executes the UPSERT query and returns the inserted/updated ID.
+func (u *UserAttributeValueUpsertOne) ID(ctx context.Context) (id int64, err error) {
+ node, err := u.create.Save(ctx)
+ if err != nil {
+ return id, err
+ }
+ return node.ID, nil
+}
+
+// IDX is like ID, but panics if an error occurs.
+func (u *UserAttributeValueUpsertOne) IDX(ctx context.Context) int64 {
+ id, err := u.ID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// UserAttributeValueCreateBulk is the builder for creating many UserAttributeValue entities in bulk.
+type UserAttributeValueCreateBulk struct {
+ config
+ err error
+ builders []*UserAttributeValueCreate
+ conflict []sql.ConflictOption
+}
+
+// Save creates the UserAttributeValue entities in the database.
+func (_c *UserAttributeValueCreateBulk) Save(ctx context.Context) ([]*UserAttributeValue, error) {
+ if _c.err != nil {
+ return nil, _c.err
+ }
+ specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
+ nodes := make([]*UserAttributeValue, len(_c.builders))
+ mutators := make([]Mutator, len(_c.builders))
+ for i := range _c.builders {
+ func(i int, root context.Context) {
+ builder := _c.builders[i]
+ builder.defaults()
+ var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
+ mutation, ok := m.(*UserAttributeValueMutation)
+ if !ok {
+ return nil, fmt.Errorf("unexpected mutation type %T", m)
+ }
+ if err := builder.check(); err != nil {
+ return nil, err
+ }
+ builder.mutation = mutation
+ var err error
+ nodes[i], specs[i] = builder.createSpec()
+ if i < len(mutators)-1 {
+ _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
+ } else {
+ spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
+ spec.OnConflict = _c.conflict
+ // Invoke the actual operation on the latest mutation in the chain.
+ if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
+ if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ }
+ }
+ if err != nil {
+ return nil, err
+ }
+ mutation.id = &nodes[i].ID
+ if specs[i].ID.Value != nil {
+ id := specs[i].ID.Value.(int64)
+ nodes[i].ID = int64(id)
+ }
+ mutation.done = true
+ return nodes[i], nil
+ })
+ for i := len(builder.hooks) - 1; i >= 0; i-- {
+ mut = builder.hooks[i](mut)
+ }
+ mutators[i] = mut
+ }(i, ctx)
+ }
+ if len(mutators) > 0 {
+ if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_c *UserAttributeValueCreateBulk) SaveX(ctx context.Context) []*UserAttributeValue {
+ v, err := _c.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return v
+}
+
+// Exec executes the query.
+func (_c *UserAttributeValueCreateBulk) Exec(ctx context.Context) error {
+ _, err := _c.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_c *UserAttributeValueCreateBulk) ExecX(ctx context.Context) {
+ if err := _c.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// OnConflict allows configuring the `ON CONFLICT` / `ON DUPLICATE KEY` clause
+// of the `INSERT` statement. For example:
+//
+// client.UserAttributeValue.CreateBulk(builders...).
+// OnConflict(
+// // Update the row with the new values
+// // the was proposed for insertion.
+// sql.ResolveWithNewValues(),
+// ).
+// // Override some of the fields with custom
+// // update values.
+// Update(func(u *ent.UserAttributeValueUpsert) {
+// SetCreatedAt(v+v).
+// }).
+// Exec(ctx)
+func (_c *UserAttributeValueCreateBulk) OnConflict(opts ...sql.ConflictOption) *UserAttributeValueUpsertBulk {
+ _c.conflict = opts
+ return &UserAttributeValueUpsertBulk{
+ create: _c,
+ }
+}
+
+// OnConflictColumns calls `OnConflict` and configures the columns
+// as conflict target. Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(sql.ConflictColumns(columns...)).
+// Exec(ctx)
+func (_c *UserAttributeValueCreateBulk) OnConflictColumns(columns ...string) *UserAttributeValueUpsertBulk {
+ _c.conflict = append(_c.conflict, sql.ConflictColumns(columns...))
+ return &UserAttributeValueUpsertBulk{
+ create: _c,
+ }
+}
+
+// UserAttributeValueUpsertBulk is the builder for "upsert"-ing
+// a bulk of UserAttributeValue nodes.
+type UserAttributeValueUpsertBulk struct {
+ create *UserAttributeValueCreateBulk
+}
+
+// UpdateNewValues updates the mutable fields using the new values that
+// were set on create. Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(
+// sql.ResolveWithNewValues(),
+// ).
+// Exec(ctx)
+func (u *UserAttributeValueUpsertBulk) UpdateNewValues() *UserAttributeValueUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithNewValues())
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(s *sql.UpdateSet) {
+ for _, b := range u.create.builders {
+ if _, exists := b.mutation.CreatedAt(); exists {
+ s.SetIgnore(userattributevalue.FieldCreatedAt)
+ }
+ }
+ }))
+ return u
+}
+
+// Ignore sets each column to itself in case of conflict.
+// Using this option is equivalent to using:
+//
+// client.UserAttributeValue.Create().
+// OnConflict(sql.ResolveWithIgnore()).
+// Exec(ctx)
+func (u *UserAttributeValueUpsertBulk) Ignore() *UserAttributeValueUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWithIgnore())
+ return u
+}
+
+// DoNothing configures the conflict_action to `DO NOTHING`.
+// Supported only by SQLite and PostgreSQL.
+func (u *UserAttributeValueUpsertBulk) DoNothing() *UserAttributeValueUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.DoNothing())
+ return u
+}
+
+// Update allows overriding fields `UPDATE` values. See the UserAttributeValueCreateBulk.OnConflict
+// documentation for more info.
+func (u *UserAttributeValueUpsertBulk) Update(set func(*UserAttributeValueUpsert)) *UserAttributeValueUpsertBulk {
+ u.create.conflict = append(u.create.conflict, sql.ResolveWith(func(update *sql.UpdateSet) {
+ set(&UserAttributeValueUpsert{UpdateSet: update})
+ }))
+ return u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (u *UserAttributeValueUpsertBulk) SetUpdatedAt(v time.Time) *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetUpdatedAt(v)
+ })
+}
+
+// UpdateUpdatedAt sets the "updated_at" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertBulk) UpdateUpdatedAt() *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateUpdatedAt()
+ })
+}
+
+// SetUserID sets the "user_id" field.
+func (u *UserAttributeValueUpsertBulk) SetUserID(v int64) *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetUserID(v)
+ })
+}
+
+// UpdateUserID sets the "user_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertBulk) UpdateUserID() *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateUserID()
+ })
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (u *UserAttributeValueUpsertBulk) SetAttributeID(v int64) *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetAttributeID(v)
+ })
+}
+
+// UpdateAttributeID sets the "attribute_id" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertBulk) UpdateAttributeID() *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateAttributeID()
+ })
+}
+
+// SetValue sets the "value" field.
+func (u *UserAttributeValueUpsertBulk) SetValue(v string) *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.SetValue(v)
+ })
+}
+
+// UpdateValue sets the "value" field to the value that was provided on create.
+func (u *UserAttributeValueUpsertBulk) UpdateValue() *UserAttributeValueUpsertBulk {
+ return u.Update(func(s *UserAttributeValueUpsert) {
+ s.UpdateValue()
+ })
+}
+
+// Exec executes the query.
+func (u *UserAttributeValueUpsertBulk) Exec(ctx context.Context) error {
+ if u.create.err != nil {
+ return u.create.err
+ }
+ for i, b := range u.create.builders {
+ if len(b.conflict) != 0 {
+ return fmt.Errorf("ent: OnConflict was set for builder %d. Set it on the UserAttributeValueCreateBulk instead", i)
+ }
+ }
+ if len(u.create.conflict) == 0 {
+ return errors.New("ent: missing options for UserAttributeValueCreateBulk.OnConflict")
+ }
+ return u.create.Exec(ctx)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (u *UserAttributeValueUpsertBulk) ExecX(ctx context.Context) {
+ if err := u.create.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/userattributevalue_delete.go b/backend/ent/userattributevalue_delete.go
new file mode 100644
index 00000000..2805e49f
--- /dev/null
+++ b/backend/ent/userattributevalue_delete.go
@@ -0,0 +1,88 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeValueDelete is the builder for deleting a UserAttributeValue entity.
+type UserAttributeValueDelete struct {
+ config
+ hooks []Hook
+ mutation *UserAttributeValueMutation
+}
+
+// Where appends a list predicates to the UserAttributeValueDelete builder.
+func (_d *UserAttributeValueDelete) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueDelete {
+ _d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query and returns how many vertices were deleted.
+func (_d *UserAttributeValueDelete) Exec(ctx context.Context) (int, error) {
+ return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserAttributeValueDelete) ExecX(ctx context.Context) int {
+ n, err := _d.Exec(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return n
+}
+
+func (_d *UserAttributeValueDelete) sqlExec(ctx context.Context) (int, error) {
+ _spec := sqlgraph.NewDeleteSpec(userattributevalue.Table, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
+ if ps := _d.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ affected, err := sqlgraph.DeleteNodes(ctx, _d.driver, _spec)
+ if err != nil && sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ _d.mutation.done = true
+ return affected, err
+}
+
+// UserAttributeValueDeleteOne is the builder for deleting a single UserAttributeValue entity.
+type UserAttributeValueDeleteOne struct {
+ _d *UserAttributeValueDelete
+}
+
+// Where appends a list predicates to the UserAttributeValueDelete builder.
+func (_d *UserAttributeValueDeleteOne) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueDeleteOne {
+ _d._d.mutation.Where(ps...)
+ return _d
+}
+
+// Exec executes the deletion query.
+func (_d *UserAttributeValueDeleteOne) Exec(ctx context.Context) error {
+ n, err := _d._d.Exec(ctx)
+ switch {
+ case err != nil:
+ return err
+ case n == 0:
+ return &NotFoundError{userattributevalue.Label}
+ default:
+ return nil
+ }
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_d *UserAttributeValueDeleteOne) ExecX(ctx context.Context) {
+ if err := _d.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/ent/userattributevalue_query.go b/backend/ent/userattributevalue_query.go
new file mode 100644
index 00000000..babfc9a9
--- /dev/null
+++ b/backend/ent/userattributevalue_query.go
@@ -0,0 +1,681 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "fmt"
+ "math"
+
+ "entgo.io/ent"
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeValueQuery is the builder for querying UserAttributeValue entities.
+type UserAttributeValueQuery struct {
+ config
+ ctx *QueryContext
+ order []userattributevalue.OrderOption
+ inters []Interceptor
+ predicates []predicate.UserAttributeValue
+ withUser *UserQuery
+ withDefinition *UserAttributeDefinitionQuery
+ // intermediate query (i.e. traversal path).
+ sql *sql.Selector
+ path func(context.Context) (*sql.Selector, error)
+}
+
+// Where adds a new predicate for the UserAttributeValueQuery builder.
+func (_q *UserAttributeValueQuery) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueQuery {
+ _q.predicates = append(_q.predicates, ps...)
+ return _q
+}
+
+// Limit the number of records to be returned by this query.
+func (_q *UserAttributeValueQuery) Limit(limit int) *UserAttributeValueQuery {
+ _q.ctx.Limit = &limit
+ return _q
+}
+
+// Offset to start from.
+func (_q *UserAttributeValueQuery) Offset(offset int) *UserAttributeValueQuery {
+ _q.ctx.Offset = &offset
+ return _q
+}
+
+// Unique configures the query builder to filter duplicate records on query.
+// By default, unique is set to true, and can be disabled using this method.
+func (_q *UserAttributeValueQuery) Unique(unique bool) *UserAttributeValueQuery {
+ _q.ctx.Unique = &unique
+ return _q
+}
+
+// Order specifies how the records should be ordered.
+func (_q *UserAttributeValueQuery) Order(o ...userattributevalue.OrderOption) *UserAttributeValueQuery {
+ _q.order = append(_q.order, o...)
+ return _q
+}
+
+// QueryUser chains the current query on the "user" edge.
+func (_q *UserAttributeValueQuery) QueryUser() *UserQuery {
+ query := (&UserClient{config: _q.config}).Query()
+ query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ selector := _q.sqlQuery(ctx)
+ if err := selector.Err(); err != nil {
+ return nil, err
+ }
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, selector),
+ sqlgraph.To(user.Table, user.FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.UserTable, userattributevalue.UserColumn),
+ )
+ fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
+ return fromU, nil
+ }
+ return query
+}
+
+// QueryDefinition chains the current query on the "definition" edge.
+func (_q *UserAttributeValueQuery) QueryDefinition() *UserAttributeDefinitionQuery {
+ query := (&UserAttributeDefinitionClient{config: _q.config}).Query()
+ query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ selector := _q.sqlQuery(ctx)
+ if err := selector.Err(); err != nil {
+ return nil, err
+ }
+ step := sqlgraph.NewStep(
+ sqlgraph.From(userattributevalue.Table, userattributevalue.FieldID, selector),
+ sqlgraph.To(userattributedefinition.Table, userattributedefinition.FieldID),
+ sqlgraph.Edge(sqlgraph.M2O, true, userattributevalue.DefinitionTable, userattributevalue.DefinitionColumn),
+ )
+ fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
+ return fromU, nil
+ }
+ return query
+}
+
+// First returns the first UserAttributeValue entity from the query.
+// Returns a *NotFoundError when no UserAttributeValue was found.
+func (_q *UserAttributeValueQuery) First(ctx context.Context) (*UserAttributeValue, error) {
+ nodes, err := _q.Limit(1).All(setContextOp(ctx, _q.ctx, ent.OpQueryFirst))
+ if err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nil, &NotFoundError{userattributevalue.Label}
+ }
+ return nodes[0], nil
+}
+
+// FirstX is like First, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) FirstX(ctx context.Context) *UserAttributeValue {
+ node, err := _q.First(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return node
+}
+
+// FirstID returns the first UserAttributeValue ID from the query.
+// Returns a *NotFoundError when no UserAttributeValue ID was found.
+func (_q *UserAttributeValueQuery) FirstID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil {
+ return
+ }
+ if len(ids) == 0 {
+ err = &NotFoundError{userattributevalue.Label}
+ return
+ }
+ return ids[0], nil
+}
+
+// FirstIDX is like FirstID, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) FirstIDX(ctx context.Context) int64 {
+ id, err := _q.FirstID(ctx)
+ if err != nil && !IsNotFound(err) {
+ panic(err)
+ }
+ return id
+}
+
+// Only returns a single UserAttributeValue entity found by the query, ensuring it only returns one.
+// Returns a *NotSingularError when more than one UserAttributeValue entity is found.
+// Returns a *NotFoundError when no UserAttributeValue entities are found.
+func (_q *UserAttributeValueQuery) Only(ctx context.Context) (*UserAttributeValue, error) {
+ nodes, err := _q.Limit(2).All(setContextOp(ctx, _q.ctx, ent.OpQueryOnly))
+ if err != nil {
+ return nil, err
+ }
+ switch len(nodes) {
+ case 1:
+ return nodes[0], nil
+ case 0:
+ return nil, &NotFoundError{userattributevalue.Label}
+ default:
+ return nil, &NotSingularError{userattributevalue.Label}
+ }
+}
+
+// OnlyX is like Only, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) OnlyX(ctx context.Context) *UserAttributeValue {
+ node, err := _q.Only(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// OnlyID is like Only, but returns the only UserAttributeValue ID in the query.
+// Returns a *NotSingularError when more than one UserAttributeValue ID is found.
+// Returns a *NotFoundError when no entities are found.
+func (_q *UserAttributeValueQuery) OnlyID(ctx context.Context) (id int64, err error) {
+ var ids []int64
+ if ids, err = _q.Limit(2).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryOnlyID)); err != nil {
+ return
+ }
+ switch len(ids) {
+ case 1:
+ id = ids[0]
+ case 0:
+ err = &NotFoundError{userattributevalue.Label}
+ default:
+ err = &NotSingularError{userattributevalue.Label}
+ }
+ return
+}
+
+// OnlyIDX is like OnlyID, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) OnlyIDX(ctx context.Context) int64 {
+ id, err := _q.OnlyID(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return id
+}
+
+// All executes the query and returns a list of UserAttributeValues.
+func (_q *UserAttributeValueQuery) All(ctx context.Context) ([]*UserAttributeValue, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ qr := querierAll[[]*UserAttributeValue, *UserAttributeValueQuery]()
+ return withInterceptors[[]*UserAttributeValue](ctx, _q, qr, _q.inters)
+}
+
+// AllX is like All, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) AllX(ctx context.Context) []*UserAttributeValue {
+ nodes, err := _q.All(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return nodes
+}
+
+// IDs executes the query and returns a list of UserAttributeValue IDs.
+func (_q *UserAttributeValueQuery) IDs(ctx context.Context) (ids []int64, err error) {
+ if _q.ctx.Unique == nil && _q.path != nil {
+ _q.Unique(true)
+ }
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs)
+ if err = _q.Select(userattributevalue.FieldID).Scan(ctx, &ids); err != nil {
+ return nil, err
+ }
+ return ids, nil
+}
+
+// IDsX is like IDs, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) IDsX(ctx context.Context) []int64 {
+ ids, err := _q.IDs(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return ids
+}
+
+// Count returns the count of the given query.
+func (_q *UserAttributeValueQuery) Count(ctx context.Context) (int, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryCount)
+ if err := _q.prepareQuery(ctx); err != nil {
+ return 0, err
+ }
+ return withInterceptors[int](ctx, _q, querierCount[*UserAttributeValueQuery](), _q.inters)
+}
+
+// CountX is like Count, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) CountX(ctx context.Context) int {
+ count, err := _q.Count(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return count
+}
+
+// Exist returns true if the query has elements in the graph.
+func (_q *UserAttributeValueQuery) Exist(ctx context.Context) (bool, error) {
+ ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist)
+ switch _, err := _q.FirstID(ctx); {
+ case IsNotFound(err):
+ return false, nil
+ case err != nil:
+ return false, fmt.Errorf("ent: check existence: %w", err)
+ default:
+ return true, nil
+ }
+}
+
+// ExistX is like Exist, but panics if an error occurs.
+func (_q *UserAttributeValueQuery) ExistX(ctx context.Context) bool {
+ exist, err := _q.Exist(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return exist
+}
+
+// Clone returns a duplicate of the UserAttributeValueQuery builder, including all associated steps. It can be
+// used to prepare common query builders and use them differently after the clone is made.
+func (_q *UserAttributeValueQuery) Clone() *UserAttributeValueQuery {
+ if _q == nil {
+ return nil
+ }
+ return &UserAttributeValueQuery{
+ config: _q.config,
+ ctx: _q.ctx.Clone(),
+ order: append([]userattributevalue.OrderOption{}, _q.order...),
+ inters: append([]Interceptor{}, _q.inters...),
+ predicates: append([]predicate.UserAttributeValue{}, _q.predicates...),
+ withUser: _q.withUser.Clone(),
+ withDefinition: _q.withDefinition.Clone(),
+ // clone intermediate query.
+ sql: _q.sql.Clone(),
+ path: _q.path,
+ }
+}
+
+// WithUser tells the query-builder to eager-load the nodes that are connected to
+// the "user" edge. The optional arguments are used to configure the query builder of the edge.
+func (_q *UserAttributeValueQuery) WithUser(opts ...func(*UserQuery)) *UserAttributeValueQuery {
+ query := (&UserClient{config: _q.config}).Query()
+ for _, opt := range opts {
+ opt(query)
+ }
+ _q.withUser = query
+ return _q
+}
+
+// WithDefinition tells the query-builder to eager-load the nodes that are connected to
+// the "definition" edge. The optional arguments are used to configure the query builder of the edge.
+func (_q *UserAttributeValueQuery) WithDefinition(opts ...func(*UserAttributeDefinitionQuery)) *UserAttributeValueQuery {
+ query := (&UserAttributeDefinitionClient{config: _q.config}).Query()
+ for _, opt := range opts {
+ opt(query)
+ }
+ _q.withDefinition = query
+ return _q
+}
+
+// GroupBy is used to group vertices by one or more fields/columns.
+// It is often used with aggregate functions, like: count, max, mean, min, sum.
+//
+// Example:
+//
+// var v []struct {
+// CreatedAt time.Time `json:"created_at,omitempty"`
+// Count int `json:"count,omitempty"`
+// }
+//
+// client.UserAttributeValue.Query().
+// GroupBy(userattributevalue.FieldCreatedAt).
+// Aggregate(ent.Count()).
+// Scan(ctx, &v)
+func (_q *UserAttributeValueQuery) GroupBy(field string, fields ...string) *UserAttributeValueGroupBy {
+ _q.ctx.Fields = append([]string{field}, fields...)
+ grbuild := &UserAttributeValueGroupBy{build: _q}
+ grbuild.flds = &_q.ctx.Fields
+ grbuild.label = userattributevalue.Label
+ grbuild.scan = grbuild.Scan
+ return grbuild
+}
+
+// Select allows the selection one or more fields/columns for the given query,
+// instead of selecting all fields in the entity.
+//
+// Example:
+//
+// var v []struct {
+// CreatedAt time.Time `json:"created_at,omitempty"`
+// }
+//
+// client.UserAttributeValue.Query().
+// Select(userattributevalue.FieldCreatedAt).
+// Scan(ctx, &v)
+func (_q *UserAttributeValueQuery) Select(fields ...string) *UserAttributeValueSelect {
+ _q.ctx.Fields = append(_q.ctx.Fields, fields...)
+ sbuild := &UserAttributeValueSelect{UserAttributeValueQuery: _q}
+ sbuild.label = userattributevalue.Label
+ sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan
+ return sbuild
+}
+
+// Aggregate returns a UserAttributeValueSelect configured with the given aggregations.
+func (_q *UserAttributeValueQuery) Aggregate(fns ...AggregateFunc) *UserAttributeValueSelect {
+ return _q.Select().Aggregate(fns...)
+}
+
+func (_q *UserAttributeValueQuery) prepareQuery(ctx context.Context) error {
+ for _, inter := range _q.inters {
+ if inter == nil {
+ return fmt.Errorf("ent: uninitialized interceptor (forgotten import ent/runtime?)")
+ }
+ if trv, ok := inter.(Traverser); ok {
+ if err := trv.Traverse(ctx, _q); err != nil {
+ return err
+ }
+ }
+ }
+ for _, f := range _q.ctx.Fields {
+ if !userattributevalue.ValidColumn(f) {
+ return &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ }
+ if _q.path != nil {
+ prev, err := _q.path(ctx)
+ if err != nil {
+ return err
+ }
+ _q.sql = prev
+ }
+ return nil
+}
+
+func (_q *UserAttributeValueQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAttributeValue, error) {
+ var (
+ nodes = []*UserAttributeValue{}
+ _spec = _q.querySpec()
+ loadedTypes = [2]bool{
+ _q.withUser != nil,
+ _q.withDefinition != nil,
+ }
+ )
+ _spec.ScanValues = func(columns []string) ([]any, error) {
+ return (*UserAttributeValue).scanValues(nil, columns)
+ }
+ _spec.Assign = func(columns []string, values []any) error {
+ node := &UserAttributeValue{config: _q.config}
+ nodes = append(nodes, node)
+ node.Edges.loadedTypes = loadedTypes
+ return node.assignValues(columns, values)
+ }
+ for i := range hooks {
+ hooks[i](ctx, _spec)
+ }
+ if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil {
+ return nil, err
+ }
+ if len(nodes) == 0 {
+ return nodes, nil
+ }
+ if query := _q.withUser; query != nil {
+ if err := _q.loadUser(ctx, query, nodes, nil,
+ func(n *UserAttributeValue, e *User) { n.Edges.User = e }); err != nil {
+ return nil, err
+ }
+ }
+ if query := _q.withDefinition; query != nil {
+ if err := _q.loadDefinition(ctx, query, nodes, nil,
+ func(n *UserAttributeValue, e *UserAttributeDefinition) { n.Edges.Definition = e }); err != nil {
+ return nil, err
+ }
+ }
+ return nodes, nil
+}
+
+func (_q *UserAttributeValueQuery) loadUser(ctx context.Context, query *UserQuery, nodes []*UserAttributeValue, init func(*UserAttributeValue), assign func(*UserAttributeValue, *User)) error {
+ ids := make([]int64, 0, len(nodes))
+ nodeids := make(map[int64][]*UserAttributeValue)
+ for i := range nodes {
+ fk := nodes[i].UserID
+ if _, ok := nodeids[fk]; !ok {
+ ids = append(ids, fk)
+ }
+ nodeids[fk] = append(nodeids[fk], nodes[i])
+ }
+ if len(ids) == 0 {
+ return nil
+ }
+ query.Where(user.IDIn(ids...))
+ neighbors, err := query.All(ctx)
+ if err != nil {
+ return err
+ }
+ for _, n := range neighbors {
+ nodes, ok := nodeids[n.ID]
+ if !ok {
+ return fmt.Errorf(`unexpected foreign-key "user_id" returned %v`, n.ID)
+ }
+ for i := range nodes {
+ assign(nodes[i], n)
+ }
+ }
+ return nil
+}
+func (_q *UserAttributeValueQuery) loadDefinition(ctx context.Context, query *UserAttributeDefinitionQuery, nodes []*UserAttributeValue, init func(*UserAttributeValue), assign func(*UserAttributeValue, *UserAttributeDefinition)) error {
+ ids := make([]int64, 0, len(nodes))
+ nodeids := make(map[int64][]*UserAttributeValue)
+ for i := range nodes {
+ fk := nodes[i].AttributeID
+ if _, ok := nodeids[fk]; !ok {
+ ids = append(ids, fk)
+ }
+ nodeids[fk] = append(nodeids[fk], nodes[i])
+ }
+ if len(ids) == 0 {
+ return nil
+ }
+ query.Where(userattributedefinition.IDIn(ids...))
+ neighbors, err := query.All(ctx)
+ if err != nil {
+ return err
+ }
+ for _, n := range neighbors {
+ nodes, ok := nodeids[n.ID]
+ if !ok {
+ return fmt.Errorf(`unexpected foreign-key "attribute_id" returned %v`, n.ID)
+ }
+ for i := range nodes {
+ assign(nodes[i], n)
+ }
+ }
+ return nil
+}
+
+func (_q *UserAttributeValueQuery) sqlCount(ctx context.Context) (int, error) {
+ _spec := _q.querySpec()
+ _spec.Node.Columns = _q.ctx.Fields
+ if len(_q.ctx.Fields) > 0 {
+ _spec.Unique = _q.ctx.Unique != nil && *_q.ctx.Unique
+ }
+ return sqlgraph.CountNodes(ctx, _q.driver, _spec)
+}
+
+func (_q *UserAttributeValueQuery) querySpec() *sqlgraph.QuerySpec {
+ _spec := sqlgraph.NewQuerySpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
+ _spec.From = _q.sql
+ if unique := _q.ctx.Unique; unique != nil {
+ _spec.Unique = *unique
+ } else if _q.path != nil {
+ _spec.Unique = true
+ }
+ if fields := _q.ctx.Fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userattributevalue.FieldID)
+ for i := range fields {
+ if fields[i] != userattributevalue.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, fields[i])
+ }
+ }
+ if _q.withUser != nil {
+ _spec.Node.AddColumnOnce(userattributevalue.FieldUserID)
+ }
+ if _q.withDefinition != nil {
+ _spec.Node.AddColumnOnce(userattributevalue.FieldAttributeID)
+ }
+ }
+ if ps := _q.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ _spec.Limit = *limit
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ _spec.Offset = *offset
+ }
+ if ps := _q.order; len(ps) > 0 {
+ _spec.Order = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ return _spec
+}
+
+func (_q *UserAttributeValueQuery) sqlQuery(ctx context.Context) *sql.Selector {
+ builder := sql.Dialect(_q.driver.Dialect())
+ t1 := builder.Table(userattributevalue.Table)
+ columns := _q.ctx.Fields
+ if len(columns) == 0 {
+ columns = userattributevalue.Columns
+ }
+ selector := builder.Select(t1.Columns(columns...)...).From(t1)
+ if _q.sql != nil {
+ selector = _q.sql
+ selector.Select(selector.Columns(columns...)...)
+ }
+ if _q.ctx.Unique != nil && *_q.ctx.Unique {
+ selector.Distinct()
+ }
+ for _, p := range _q.predicates {
+ p(selector)
+ }
+ for _, p := range _q.order {
+ p(selector)
+ }
+ if offset := _q.ctx.Offset; offset != nil {
+ // limit is mandatory for offset clause. We start
+ // with default value, and override it below if needed.
+ selector.Offset(*offset).Limit(math.MaxInt32)
+ }
+ if limit := _q.ctx.Limit; limit != nil {
+ selector.Limit(*limit)
+ }
+ return selector
+}
+
+// UserAttributeValueGroupBy is the group-by builder for UserAttributeValue entities.
+type UserAttributeValueGroupBy struct {
+ selector
+ build *UserAttributeValueQuery
+}
+
+// Aggregate adds the given aggregation functions to the group-by query.
+func (_g *UserAttributeValueGroupBy) Aggregate(fns ...AggregateFunc) *UserAttributeValueGroupBy {
+ _g.fns = append(_g.fns, fns...)
+ return _g
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_g *UserAttributeValueGroupBy) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _g.build.ctx, ent.OpQueryGroupBy)
+ if err := _g.build.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserAttributeValueQuery, *UserAttributeValueGroupBy](ctx, _g.build, _g, _g.build.inters, v)
+}
+
+func (_g *UserAttributeValueGroupBy) sqlScan(ctx context.Context, root *UserAttributeValueQuery, v any) error {
+ selector := root.sqlQuery(ctx).Select()
+ aggregation := make([]string, 0, len(_g.fns))
+ for _, fn := range _g.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ if len(selector.SelectedColumns()) == 0 {
+ columns := make([]string, 0, len(*_g.flds)+len(_g.fns))
+ for _, f := range *_g.flds {
+ columns = append(columns, selector.C(f))
+ }
+ columns = append(columns, aggregation...)
+ selector.Select(columns...)
+ }
+ selector.GroupBy(selector.Columns(*_g.flds...)...)
+ if err := selector.Err(); err != nil {
+ return err
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _g.build.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
+
+// UserAttributeValueSelect is the builder for selecting fields of UserAttributeValue entities.
+type UserAttributeValueSelect struct {
+ *UserAttributeValueQuery
+ selector
+}
+
+// Aggregate adds the given aggregation functions to the selector query.
+func (_s *UserAttributeValueSelect) Aggregate(fns ...AggregateFunc) *UserAttributeValueSelect {
+ _s.fns = append(_s.fns, fns...)
+ return _s
+}
+
+// Scan applies the selector query and scans the result into the given value.
+func (_s *UserAttributeValueSelect) Scan(ctx context.Context, v any) error {
+ ctx = setContextOp(ctx, _s.ctx, ent.OpQuerySelect)
+ if err := _s.prepareQuery(ctx); err != nil {
+ return err
+ }
+ return scanWithInterceptors[*UserAttributeValueQuery, *UserAttributeValueSelect](ctx, _s.UserAttributeValueQuery, _s, _s.inters, v)
+}
+
+func (_s *UserAttributeValueSelect) sqlScan(ctx context.Context, root *UserAttributeValueQuery, v any) error {
+ selector := root.sqlQuery(ctx)
+ aggregation := make([]string, 0, len(_s.fns))
+ for _, fn := range _s.fns {
+ aggregation = append(aggregation, fn(selector))
+ }
+ switch n := len(*_s.selector.flds); {
+ case n == 0 && len(aggregation) > 0:
+ selector.Select(aggregation...)
+ case n != 0 && len(aggregation) > 0:
+ selector.AppendSelect(aggregation...)
+ }
+ rows := &sql.Rows{}
+ query, args := selector.Query()
+ if err := _s.driver.Query(ctx, query, args, rows); err != nil {
+ return err
+ }
+ defer rows.Close()
+ return sql.ScanSlice(rows, v)
+}
diff --git a/backend/ent/userattributevalue_update.go b/backend/ent/userattributevalue_update.go
new file mode 100644
index 00000000..7dfce024
--- /dev/null
+++ b/backend/ent/userattributevalue_update.go
@@ -0,0 +1,504 @@
+// Code generated by ent, DO NOT EDIT.
+
+package ent
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "entgo.io/ent/dialect/sql"
+ "entgo.io/ent/dialect/sql/sqlgraph"
+ "entgo.io/ent/schema/field"
+ "github.com/Wei-Shaw/sub2api/ent/predicate"
+ "github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+)
+
+// UserAttributeValueUpdate is the builder for updating UserAttributeValue entities.
+type UserAttributeValueUpdate struct {
+ config
+ hooks []Hook
+ mutation *UserAttributeValueMutation
+}
+
+// Where appends a list predicates to the UserAttributeValueUpdate builder.
+func (_u *UserAttributeValueUpdate) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueUpdate {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *UserAttributeValueUpdate) SetUpdatedAt(v time.Time) *UserAttributeValueUpdate {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// SetUserID sets the "user_id" field.
+func (_u *UserAttributeValueUpdate) SetUserID(v int64) *UserAttributeValueUpdate {
+ _u.mutation.SetUserID(v)
+ return _u
+}
+
+// SetNillableUserID sets the "user_id" field if the given value is not nil.
+func (_u *UserAttributeValueUpdate) SetNillableUserID(v *int64) *UserAttributeValueUpdate {
+ if v != nil {
+ _u.SetUserID(*v)
+ }
+ return _u
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (_u *UserAttributeValueUpdate) SetAttributeID(v int64) *UserAttributeValueUpdate {
+ _u.mutation.SetAttributeID(v)
+ return _u
+}
+
+// SetNillableAttributeID sets the "attribute_id" field if the given value is not nil.
+func (_u *UserAttributeValueUpdate) SetNillableAttributeID(v *int64) *UserAttributeValueUpdate {
+ if v != nil {
+ _u.SetAttributeID(*v)
+ }
+ return _u
+}
+
+// SetValue sets the "value" field.
+func (_u *UserAttributeValueUpdate) SetValue(v string) *UserAttributeValueUpdate {
+ _u.mutation.SetValue(v)
+ return _u
+}
+
+// SetNillableValue sets the "value" field if the given value is not nil.
+func (_u *UserAttributeValueUpdate) SetNillableValue(v *string) *UserAttributeValueUpdate {
+ if v != nil {
+ _u.SetValue(*v)
+ }
+ return _u
+}
+
+// SetUser sets the "user" edge to the User entity.
+func (_u *UserAttributeValueUpdate) SetUser(v *User) *UserAttributeValueUpdate {
+ return _u.SetUserID(v.ID)
+}
+
+// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
+func (_u *UserAttributeValueUpdate) SetDefinitionID(id int64) *UserAttributeValueUpdate {
+ _u.mutation.SetDefinitionID(id)
+ return _u
+}
+
+// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
+func (_u *UserAttributeValueUpdate) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueUpdate {
+ return _u.SetDefinitionID(v.ID)
+}
+
+// Mutation returns the UserAttributeValueMutation object of the builder.
+func (_u *UserAttributeValueUpdate) Mutation() *UserAttributeValueMutation {
+ return _u.mutation
+}
+
+// ClearUser clears the "user" edge to the User entity.
+func (_u *UserAttributeValueUpdate) ClearUser() *UserAttributeValueUpdate {
+ _u.mutation.ClearUser()
+ return _u
+}
+
+// ClearDefinition clears the "definition" edge to the UserAttributeDefinition entity.
+func (_u *UserAttributeValueUpdate) ClearDefinition() *UserAttributeValueUpdate {
+ _u.mutation.ClearDefinition()
+ return _u
+}
+
+// Save executes the query and returns the number of nodes affected by the update operation.
+func (_u *UserAttributeValueUpdate) Save(ctx context.Context) (int, error) {
+ _u.defaults()
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserAttributeValueUpdate) SaveX(ctx context.Context) int {
+ affected, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return affected
+}
+
+// Exec executes the query.
+func (_u *UserAttributeValueUpdate) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserAttributeValueUpdate) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *UserAttributeValueUpdate) defaults() {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ v := userattributevalue.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *UserAttributeValueUpdate) check() error {
+ if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
+ return errors.New(`ent: clearing a required unique edge "UserAttributeValue.user"`)
+ }
+ if _u.mutation.DefinitionCleared() && len(_u.mutation.DefinitionIDs()) > 0 {
+ return errors.New(`ent: clearing a required unique edge "UserAttributeValue.definition"`)
+ }
+ return nil
+}
+
+func (_u *UserAttributeValueUpdate) sqlSave(ctx context.Context) (_node int, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
+ }
+ if value, ok := _u.mutation.Value(); ok {
+ _spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
+ }
+ if _u.mutation.UserCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.UserTable,
+ Columns: []string{userattributevalue.UserColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.UserTable,
+ Columns: []string{userattributevalue.UserColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ if _u.mutation.DefinitionCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.DefinitionTable,
+ Columns: []string{userattributevalue.DefinitionColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.DefinitionIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.DefinitionTable,
+ Columns: []string{userattributevalue.DefinitionColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userattributevalue.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return 0, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
+
+// UserAttributeValueUpdateOne is the builder for updating a single UserAttributeValue entity.
+type UserAttributeValueUpdateOne struct {
+ config
+ fields []string
+ hooks []Hook
+ mutation *UserAttributeValueMutation
+}
+
+// SetUpdatedAt sets the "updated_at" field.
+func (_u *UserAttributeValueUpdateOne) SetUpdatedAt(v time.Time) *UserAttributeValueUpdateOne {
+ _u.mutation.SetUpdatedAt(v)
+ return _u
+}
+
+// SetUserID sets the "user_id" field.
+func (_u *UserAttributeValueUpdateOne) SetUserID(v int64) *UserAttributeValueUpdateOne {
+ _u.mutation.SetUserID(v)
+ return _u
+}
+
+// SetNillableUserID sets the "user_id" field if the given value is not nil.
+func (_u *UserAttributeValueUpdateOne) SetNillableUserID(v *int64) *UserAttributeValueUpdateOne {
+ if v != nil {
+ _u.SetUserID(*v)
+ }
+ return _u
+}
+
+// SetAttributeID sets the "attribute_id" field.
+func (_u *UserAttributeValueUpdateOne) SetAttributeID(v int64) *UserAttributeValueUpdateOne {
+ _u.mutation.SetAttributeID(v)
+ return _u
+}
+
+// SetNillableAttributeID sets the "attribute_id" field if the given value is not nil.
+func (_u *UserAttributeValueUpdateOne) SetNillableAttributeID(v *int64) *UserAttributeValueUpdateOne {
+ if v != nil {
+ _u.SetAttributeID(*v)
+ }
+ return _u
+}
+
+// SetValue sets the "value" field.
+func (_u *UserAttributeValueUpdateOne) SetValue(v string) *UserAttributeValueUpdateOne {
+ _u.mutation.SetValue(v)
+ return _u
+}
+
+// SetNillableValue sets the "value" field if the given value is not nil.
+func (_u *UserAttributeValueUpdateOne) SetNillableValue(v *string) *UserAttributeValueUpdateOne {
+ if v != nil {
+ _u.SetValue(*v)
+ }
+ return _u
+}
+
+// SetUser sets the "user" edge to the User entity.
+func (_u *UserAttributeValueUpdateOne) SetUser(v *User) *UserAttributeValueUpdateOne {
+ return _u.SetUserID(v.ID)
+}
+
+// SetDefinitionID sets the "definition" edge to the UserAttributeDefinition entity by ID.
+func (_u *UserAttributeValueUpdateOne) SetDefinitionID(id int64) *UserAttributeValueUpdateOne {
+ _u.mutation.SetDefinitionID(id)
+ return _u
+}
+
+// SetDefinition sets the "definition" edge to the UserAttributeDefinition entity.
+func (_u *UserAttributeValueUpdateOne) SetDefinition(v *UserAttributeDefinition) *UserAttributeValueUpdateOne {
+ return _u.SetDefinitionID(v.ID)
+}
+
+// Mutation returns the UserAttributeValueMutation object of the builder.
+func (_u *UserAttributeValueUpdateOne) Mutation() *UserAttributeValueMutation {
+ return _u.mutation
+}
+
+// ClearUser clears the "user" edge to the User entity.
+func (_u *UserAttributeValueUpdateOne) ClearUser() *UserAttributeValueUpdateOne {
+ _u.mutation.ClearUser()
+ return _u
+}
+
+// ClearDefinition clears the "definition" edge to the UserAttributeDefinition entity.
+func (_u *UserAttributeValueUpdateOne) ClearDefinition() *UserAttributeValueUpdateOne {
+ _u.mutation.ClearDefinition()
+ return _u
+}
+
+// Where appends a list predicates to the UserAttributeValueUpdate builder.
+func (_u *UserAttributeValueUpdateOne) Where(ps ...predicate.UserAttributeValue) *UserAttributeValueUpdateOne {
+ _u.mutation.Where(ps...)
+ return _u
+}
+
+// Select allows selecting one or more fields (columns) of the returned entity.
+// The default is selecting all fields defined in the entity schema.
+func (_u *UserAttributeValueUpdateOne) Select(field string, fields ...string) *UserAttributeValueUpdateOne {
+ _u.fields = append([]string{field}, fields...)
+ return _u
+}
+
+// Save executes the query and returns the updated UserAttributeValue entity.
+func (_u *UserAttributeValueUpdateOne) Save(ctx context.Context) (*UserAttributeValue, error) {
+ _u.defaults()
+ return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
+}
+
+// SaveX is like Save, but panics if an error occurs.
+func (_u *UserAttributeValueUpdateOne) SaveX(ctx context.Context) *UserAttributeValue {
+ node, err := _u.Save(ctx)
+ if err != nil {
+ panic(err)
+ }
+ return node
+}
+
+// Exec executes the query on the entity.
+func (_u *UserAttributeValueUpdateOne) Exec(ctx context.Context) error {
+ _, err := _u.Save(ctx)
+ return err
+}
+
+// ExecX is like Exec, but panics if an error occurs.
+func (_u *UserAttributeValueUpdateOne) ExecX(ctx context.Context) {
+ if err := _u.Exec(ctx); err != nil {
+ panic(err)
+ }
+}
+
+// defaults sets the default values of the builder before save.
+func (_u *UserAttributeValueUpdateOne) defaults() {
+ if _, ok := _u.mutation.UpdatedAt(); !ok {
+ v := userattributevalue.UpdateDefaultUpdatedAt()
+ _u.mutation.SetUpdatedAt(v)
+ }
+}
+
+// check runs all checks and user-defined validators on the builder.
+func (_u *UserAttributeValueUpdateOne) check() error {
+ if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
+ return errors.New(`ent: clearing a required unique edge "UserAttributeValue.user"`)
+ }
+ if _u.mutation.DefinitionCleared() && len(_u.mutation.DefinitionIDs()) > 0 {
+ return errors.New(`ent: clearing a required unique edge "UserAttributeValue.definition"`)
+ }
+ return nil
+}
+
+func (_u *UserAttributeValueUpdateOne) sqlSave(ctx context.Context) (_node *UserAttributeValue, err error) {
+ if err := _u.check(); err != nil {
+ return _node, err
+ }
+ _spec := sqlgraph.NewUpdateSpec(userattributevalue.Table, userattributevalue.Columns, sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64))
+ id, ok := _u.mutation.ID()
+ if !ok {
+ return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAttributeValue.id" for update`)}
+ }
+ _spec.Node.ID.Value = id
+ if fields := _u.fields; len(fields) > 0 {
+ _spec.Node.Columns = make([]string, 0, len(fields))
+ _spec.Node.Columns = append(_spec.Node.Columns, userattributevalue.FieldID)
+ for _, f := range fields {
+ if !userattributevalue.ValidColumn(f) {
+ return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)}
+ }
+ if f != userattributevalue.FieldID {
+ _spec.Node.Columns = append(_spec.Node.Columns, f)
+ }
+ }
+ }
+ if ps := _u.mutation.predicates; len(ps) > 0 {
+ _spec.Predicate = func(selector *sql.Selector) {
+ for i := range ps {
+ ps[i](selector)
+ }
+ }
+ }
+ if value, ok := _u.mutation.UpdatedAt(); ok {
+ _spec.SetField(userattributevalue.FieldUpdatedAt, field.TypeTime, value)
+ }
+ if value, ok := _u.mutation.Value(); ok {
+ _spec.SetField(userattributevalue.FieldValue, field.TypeString, value)
+ }
+ if _u.mutation.UserCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.UserTable,
+ Columns: []string{userattributevalue.UserColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.UserTable,
+ Columns: []string{userattributevalue.UserColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ if _u.mutation.DefinitionCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.DefinitionTable,
+ Columns: []string{userattributevalue.DefinitionColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.DefinitionIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.M2O,
+ Inverse: true,
+ Table: userattributevalue.DefinitionTable,
+ Columns: []string{userattributevalue.DefinitionColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributedefinition.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
+ _node = &UserAttributeValue{config: _u.config}
+ _spec.Assign = _node.assignValues
+ _spec.ScanValues = _node.scanValues
+ if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
+ if _, ok := err.(*sqlgraph.NotFoundError); ok {
+ err = &NotFoundError{userattributevalue.Label}
+ } else if sqlgraph.IsConstraintError(err) {
+ err = &ConstraintError{msg: err.Error(), wrap: err}
+ }
+ return nil, err
+ }
+ _u.mutation.done = true
+ return _node, nil
+}
diff --git a/backend/internal/handler/admin/user_attribute_handler.go b/backend/internal/handler/admin/user_attribute_handler.go
new file mode 100644
index 00000000..2f326279
--- /dev/null
+++ b/backend/internal/handler/admin/user_attribute_handler.go
@@ -0,0 +1,342 @@
+package admin
+
+import (
+ "strconv"
+
+ "github.com/Wei-Shaw/sub2api/internal/pkg/response"
+ "github.com/Wei-Shaw/sub2api/internal/service"
+
+ "github.com/gin-gonic/gin"
+)
+
+// UserAttributeHandler handles user attribute management
+type UserAttributeHandler struct {
+ attrService *service.UserAttributeService
+}
+
+// NewUserAttributeHandler creates a new handler
+func NewUserAttributeHandler(attrService *service.UserAttributeService) *UserAttributeHandler {
+ return &UserAttributeHandler{attrService: attrService}
+}
+
+// --- Request/Response DTOs ---
+
+// CreateAttributeDefinitionRequest represents create attribute definition request
+type CreateAttributeDefinitionRequest struct {
+ Key string `json:"key" binding:"required,min=1,max=100"`
+ Name string `json:"name" binding:"required,min=1,max=255"`
+ Description string `json:"description"`
+ Type string `json:"type" binding:"required"`
+ Options []service.UserAttributeOption `json:"options"`
+ Required bool `json:"required"`
+ Validation service.UserAttributeValidation `json:"validation"`
+ Placeholder string `json:"placeholder"`
+ Enabled bool `json:"enabled"`
+}
+
+// UpdateAttributeDefinitionRequest represents update attribute definition request
+type UpdateAttributeDefinitionRequest struct {
+ Name *string `json:"name"`
+ Description *string `json:"description"`
+ Type *string `json:"type"`
+ Options *[]service.UserAttributeOption `json:"options"`
+ Required *bool `json:"required"`
+ Validation *service.UserAttributeValidation `json:"validation"`
+ Placeholder *string `json:"placeholder"`
+ Enabled *bool `json:"enabled"`
+}
+
+// ReorderRequest represents reorder attribute definitions request
+type ReorderRequest struct {
+ IDs []int64 `json:"ids" binding:"required"`
+}
+
+// UpdateUserAttributesRequest represents update user attributes request
+type UpdateUserAttributesRequest struct {
+ Values map[int64]string `json:"values" binding:"required"`
+}
+
+// BatchGetUserAttributesRequest represents batch get user attributes request
+type BatchGetUserAttributesRequest struct {
+ UserIDs []int64 `json:"user_ids" binding:"required"`
+}
+
+// BatchUserAttributesResponse represents batch user attributes response
+type BatchUserAttributesResponse struct {
+ // Map of userID -> map of attributeID -> value
+ Attributes map[int64]map[int64]string `json:"attributes"`
+}
+
+// AttributeDefinitionResponse represents attribute definition response
+type AttributeDefinitionResponse struct {
+ ID int64 `json:"id"`
+ Key string `json:"key"`
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Type string `json:"type"`
+ Options []service.UserAttributeOption `json:"options"`
+ Required bool `json:"required"`
+ Validation service.UserAttributeValidation `json:"validation"`
+ Placeholder string `json:"placeholder"`
+ DisplayOrder int `json:"display_order"`
+ Enabled bool `json:"enabled"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
+}
+
+// AttributeValueResponse represents attribute value response
+type AttributeValueResponse struct {
+ ID int64 `json:"id"`
+ UserID int64 `json:"user_id"`
+ AttributeID int64 `json:"attribute_id"`
+ Value string `json:"value"`
+ CreatedAt string `json:"created_at"`
+ UpdatedAt string `json:"updated_at"`
+}
+
+// --- Helpers ---
+
+func defToResponse(def *service.UserAttributeDefinition) *AttributeDefinitionResponse {
+ return &AttributeDefinitionResponse{
+ ID: def.ID,
+ Key: def.Key,
+ Name: def.Name,
+ Description: def.Description,
+ Type: string(def.Type),
+ Options: def.Options,
+ Required: def.Required,
+ Validation: def.Validation,
+ Placeholder: def.Placeholder,
+ DisplayOrder: def.DisplayOrder,
+ Enabled: def.Enabled,
+ CreatedAt: def.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
+ UpdatedAt: def.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
+ }
+}
+
+func valueToResponse(val *service.UserAttributeValue) *AttributeValueResponse {
+ return &AttributeValueResponse{
+ ID: val.ID,
+ UserID: val.UserID,
+ AttributeID: val.AttributeID,
+ Value: val.Value,
+ CreatedAt: val.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
+ UpdatedAt: val.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
+ }
+}
+
+// --- Handlers ---
+
+// ListDefinitions lists all attribute definitions
+// GET /admin/user-attributes
+func (h *UserAttributeHandler) ListDefinitions(c *gin.Context) {
+ enabledOnly := c.Query("enabled") == "true"
+
+ defs, err := h.attrService.ListDefinitions(c.Request.Context(), enabledOnly)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ out := make([]*AttributeDefinitionResponse, 0, len(defs))
+ for i := range defs {
+ out = append(out, defToResponse(&defs[i]))
+ }
+
+ response.Success(c, out)
+}
+
+// CreateDefinition creates a new attribute definition
+// POST /admin/user-attributes
+func (h *UserAttributeHandler) CreateDefinition(c *gin.Context) {
+ var req CreateAttributeDefinitionRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ def, err := h.attrService.CreateDefinition(c.Request.Context(), service.CreateAttributeDefinitionInput{
+ Key: req.Key,
+ Name: req.Name,
+ Description: req.Description,
+ Type: service.UserAttributeType(req.Type),
+ Options: req.Options,
+ Required: req.Required,
+ Validation: req.Validation,
+ Placeholder: req.Placeholder,
+ Enabled: req.Enabled,
+ })
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Success(c, defToResponse(def))
+}
+
+// UpdateDefinition updates an attribute definition
+// PUT /admin/user-attributes/:id
+func (h *UserAttributeHandler) UpdateDefinition(c *gin.Context) {
+ id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.BadRequest(c, "Invalid attribute ID")
+ return
+ }
+
+ var req UpdateAttributeDefinitionRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ input := service.UpdateAttributeDefinitionInput{
+ Name: req.Name,
+ Description: req.Description,
+ Options: req.Options,
+ Required: req.Required,
+ Validation: req.Validation,
+ Placeholder: req.Placeholder,
+ Enabled: req.Enabled,
+ }
+ if req.Type != nil {
+ t := service.UserAttributeType(*req.Type)
+ input.Type = &t
+ }
+
+ def, err := h.attrService.UpdateDefinition(c.Request.Context(), id, input)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Success(c, defToResponse(def))
+}
+
+// DeleteDefinition deletes an attribute definition
+// DELETE /admin/user-attributes/:id
+func (h *UserAttributeHandler) DeleteDefinition(c *gin.Context) {
+ id, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.BadRequest(c, "Invalid attribute ID")
+ return
+ }
+
+ if err := h.attrService.DeleteDefinition(c.Request.Context(), id); err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Success(c, gin.H{"message": "Attribute definition deleted successfully"})
+}
+
+// ReorderDefinitions reorders attribute definitions
+// PUT /admin/user-attributes/reorder
+func (h *UserAttributeHandler) ReorderDefinitions(c *gin.Context) {
+ var req ReorderRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ // Convert IDs array to orders map (position in array = display_order)
+ orders := make(map[int64]int, len(req.IDs))
+ for i, id := range req.IDs {
+ orders[id] = i
+ }
+
+ if err := h.attrService.ReorderDefinitions(c.Request.Context(), orders); err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Success(c, gin.H{"message": "Reorder successful"})
+}
+
+// GetUserAttributes gets a user's attribute values
+// GET /admin/users/:id/attributes
+func (h *UserAttributeHandler) GetUserAttributes(c *gin.Context) {
+ userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.BadRequest(c, "Invalid user ID")
+ return
+ }
+
+ values, err := h.attrService.GetUserAttributes(c.Request.Context(), userID)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ out := make([]*AttributeValueResponse, 0, len(values))
+ for i := range values {
+ out = append(out, valueToResponse(&values[i]))
+ }
+
+ response.Success(c, out)
+}
+
+// UpdateUserAttributes updates a user's attribute values
+// PUT /admin/users/:id/attributes
+func (h *UserAttributeHandler) UpdateUserAttributes(c *gin.Context) {
+ userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
+ if err != nil {
+ response.BadRequest(c, "Invalid user ID")
+ return
+ }
+
+ var req UpdateUserAttributesRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ inputs := make([]service.UpdateUserAttributeInput, 0, len(req.Values))
+ for attrID, value := range req.Values {
+ inputs = append(inputs, service.UpdateUserAttributeInput{
+ AttributeID: attrID,
+ Value: value,
+ })
+ }
+
+ if err := h.attrService.UpdateUserAttributes(c.Request.Context(), userID, inputs); err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ // Return updated values
+ values, err := h.attrService.GetUserAttributes(c.Request.Context(), userID)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ out := make([]*AttributeValueResponse, 0, len(values))
+ for i := range values {
+ out = append(out, valueToResponse(&values[i]))
+ }
+
+ response.Success(c, out)
+}
+
+// GetBatchUserAttributes gets attribute values for multiple users
+// POST /admin/user-attributes/batch
+func (h *UserAttributeHandler) GetBatchUserAttributes(c *gin.Context) {
+ var req BatchGetUserAttributesRequest
+ if err := c.ShouldBindJSON(&req); err != nil {
+ response.BadRequest(c, "Invalid request: "+err.Error())
+ return
+ }
+
+ if len(req.UserIDs) == 0 {
+ response.Success(c, BatchUserAttributesResponse{Attributes: map[int64]map[int64]string{}})
+ return
+ }
+
+ attrs, err := h.attrService.GetBatchUserAttributes(c.Request.Context(), req.UserIDs)
+ if err != nil {
+ response.ErrorFrom(c, err)
+ return
+ }
+
+ response.Success(c, BatchUserAttributesResponse{Attributes: attrs})
+}
diff --git a/backend/internal/handler/handler.go b/backend/internal/handler/handler.go
index 85105a30..817b71d3 100644
--- a/backend/internal/handler/handler.go
+++ b/backend/internal/handler/handler.go
@@ -20,6 +20,7 @@ type AdminHandlers struct {
System *admin.SystemHandler
Subscription *admin.SubscriptionHandler
Usage *admin.UsageHandler
+ UserAttribute *admin.UserAttributeHandler
}
// Handlers contains all HTTP handlers
diff --git a/backend/internal/handler/wire.go b/backend/internal/handler/wire.go
index fc9f1642..1695f8a9 100644
--- a/backend/internal/handler/wire.go
+++ b/backend/internal/handler/wire.go
@@ -23,6 +23,7 @@ func ProvideAdminHandlers(
systemHandler *admin.SystemHandler,
subscriptionHandler *admin.SubscriptionHandler,
usageHandler *admin.UsageHandler,
+ userAttributeHandler *admin.UserAttributeHandler,
) *AdminHandlers {
return &AdminHandlers{
Dashboard: dashboardHandler,
@@ -39,6 +40,7 @@ func ProvideAdminHandlers(
System: systemHandler,
Subscription: subscriptionHandler,
Usage: usageHandler,
+ UserAttribute: userAttributeHandler,
}
}
@@ -107,6 +109,7 @@ var ProviderSet = wire.NewSet(
ProvideSystemHandler,
admin.NewSubscriptionHandler,
admin.NewUsageHandler,
+ admin.NewUserAttributeHandler,
// AdminHandlers and Handlers constructors
ProvideAdminHandlers,
diff --git a/backend/internal/repository/user_attribute_repo.go b/backend/internal/repository/user_attribute_repo.go
new file mode 100644
index 00000000..504aa91e
--- /dev/null
+++ b/backend/internal/repository/user_attribute_repo.go
@@ -0,0 +1,387 @@
+package repository
+
+import (
+ "context"
+ "database/sql"
+
+ dbent "github.com/Wei-Shaw/sub2api/ent"
+ "github.com/Wei-Shaw/sub2api/ent/userattributedefinition"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
+ "github.com/Wei-Shaw/sub2api/internal/service"
+)
+
+// UserAttributeDefinitionRepository implementation
+type userAttributeDefinitionRepository struct {
+ client *dbent.Client
+}
+
+// NewUserAttributeDefinitionRepository creates a new repository instance
+func NewUserAttributeDefinitionRepository(client *dbent.Client) service.UserAttributeDefinitionRepository {
+ return &userAttributeDefinitionRepository{client: client}
+}
+
+func (r *userAttributeDefinitionRepository) Create(ctx context.Context, def *service.UserAttributeDefinition) error {
+ client := clientFromContext(ctx, r.client)
+
+ created, err := client.UserAttributeDefinition.Create().
+ SetKey(def.Key).
+ SetName(def.Name).
+ SetDescription(def.Description).
+ SetType(string(def.Type)).
+ SetOptions(toEntOptions(def.Options)).
+ SetRequired(def.Required).
+ SetValidation(toEntValidation(def.Validation)).
+ SetPlaceholder(def.Placeholder).
+ SetEnabled(def.Enabled).
+ Save(ctx)
+
+ if err != nil {
+ return translatePersistenceError(err, nil, service.ErrAttributeKeyExists)
+ }
+
+ def.ID = created.ID
+ def.DisplayOrder = created.DisplayOrder
+ def.CreatedAt = created.CreatedAt
+ def.UpdatedAt = created.UpdatedAt
+ return nil
+}
+
+func (r *userAttributeDefinitionRepository) GetByID(ctx context.Context, id int64) (*service.UserAttributeDefinition, error) {
+ client := clientFromContext(ctx, r.client)
+
+ e, err := client.UserAttributeDefinition.Query().
+ Where(userattributedefinition.IDEQ(id)).
+ Only(ctx)
+ if err != nil {
+ return nil, translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
+ }
+ return defEntityToService(e), nil
+}
+
+func (r *userAttributeDefinitionRepository) GetByKey(ctx context.Context, key string) (*service.UserAttributeDefinition, error) {
+ client := clientFromContext(ctx, r.client)
+
+ e, err := client.UserAttributeDefinition.Query().
+ Where(userattributedefinition.KeyEQ(key)).
+ Only(ctx)
+ if err != nil {
+ return nil, translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
+ }
+ return defEntityToService(e), nil
+}
+
+func (r *userAttributeDefinitionRepository) Update(ctx context.Context, def *service.UserAttributeDefinition) error {
+ client := clientFromContext(ctx, r.client)
+
+ updated, err := client.UserAttributeDefinition.UpdateOneID(def.ID).
+ SetName(def.Name).
+ SetDescription(def.Description).
+ SetType(string(def.Type)).
+ SetOptions(toEntOptions(def.Options)).
+ SetRequired(def.Required).
+ SetValidation(toEntValidation(def.Validation)).
+ SetPlaceholder(def.Placeholder).
+ SetDisplayOrder(def.DisplayOrder).
+ SetEnabled(def.Enabled).
+ Save(ctx)
+
+ if err != nil {
+ return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, service.ErrAttributeKeyExists)
+ }
+
+ def.UpdatedAt = updated.UpdatedAt
+ return nil
+}
+
+func (r *userAttributeDefinitionRepository) Delete(ctx context.Context, id int64) error {
+ client := clientFromContext(ctx, r.client)
+
+ _, err := client.UserAttributeDefinition.Delete().
+ Where(userattributedefinition.IDEQ(id)).
+ Exec(ctx)
+ return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
+}
+
+func (r *userAttributeDefinitionRepository) List(ctx context.Context, enabledOnly bool) ([]service.UserAttributeDefinition, error) {
+ client := clientFromContext(ctx, r.client)
+
+ q := client.UserAttributeDefinition.Query()
+ if enabledOnly {
+ q = q.Where(userattributedefinition.EnabledEQ(true))
+ }
+
+ entities, err := q.Order(dbent.Asc(userattributedefinition.FieldDisplayOrder)).All(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ result := make([]service.UserAttributeDefinition, 0, len(entities))
+ for _, e := range entities {
+ result = append(result, *defEntityToService(e))
+ }
+ return result, nil
+}
+
+func (r *userAttributeDefinitionRepository) UpdateDisplayOrders(ctx context.Context, orders map[int64]int) error {
+ tx, err := r.client.Tx(ctx)
+ if err != nil {
+ return err
+ }
+ defer func() { _ = tx.Rollback() }()
+
+ for id, order := range orders {
+ if _, err := tx.UserAttributeDefinition.UpdateOneID(id).
+ SetDisplayOrder(order).
+ Save(ctx); err != nil {
+ return translatePersistenceError(err, service.ErrAttributeDefinitionNotFound, nil)
+ }
+ }
+
+ return tx.Commit()
+}
+
+func (r *userAttributeDefinitionRepository) ExistsByKey(ctx context.Context, key string) (bool, error) {
+ client := clientFromContext(ctx, r.client)
+ return client.UserAttributeDefinition.Query().
+ Where(userattributedefinition.KeyEQ(key)).
+ Exist(ctx)
+}
+
+// UserAttributeValueRepository implementation
+type userAttributeValueRepository struct {
+ client *dbent.Client
+ sql *sql.DB
+}
+
+// NewUserAttributeValueRepository creates a new repository instance
+func NewUserAttributeValueRepository(client *dbent.Client, sqlDB *sql.DB) service.UserAttributeValueRepository {
+ return &userAttributeValueRepository{client: client, sql: sqlDB}
+}
+
+func (r *userAttributeValueRepository) GetByUserID(ctx context.Context, userID int64) ([]service.UserAttributeValue, error) {
+ client := clientFromContext(ctx, r.client)
+
+ entities, err := client.UserAttributeValue.Query().
+ Where(userattributevalue.UserIDEQ(userID)).
+ All(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ result := make([]service.UserAttributeValue, 0, len(entities))
+ for _, e := range entities {
+ result = append(result, service.UserAttributeValue{
+ ID: e.ID,
+ UserID: e.UserID,
+ AttributeID: e.AttributeID,
+ Value: e.Value,
+ CreatedAt: e.CreatedAt,
+ UpdatedAt: e.UpdatedAt,
+ })
+ }
+ return result, nil
+}
+
+func (r *userAttributeValueRepository) GetByUserIDs(ctx context.Context, userIDs []int64) ([]service.UserAttributeValue, error) {
+ if len(userIDs) == 0 {
+ return []service.UserAttributeValue{}, nil
+ }
+
+ client := clientFromContext(ctx, r.client)
+
+ entities, err := client.UserAttributeValue.Query().
+ Where(userattributevalue.UserIDIn(userIDs...)).
+ All(ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ result := make([]service.UserAttributeValue, 0, len(entities))
+ for _, e := range entities {
+ result = append(result, service.UserAttributeValue{
+ ID: e.ID,
+ UserID: e.UserID,
+ AttributeID: e.AttributeID,
+ Value: e.Value,
+ CreatedAt: e.CreatedAt,
+ UpdatedAt: e.UpdatedAt,
+ })
+ }
+ return result, nil
+}
+
+func (r *userAttributeValueRepository) UpsertBatch(ctx context.Context, userID int64, inputs []service.UpdateUserAttributeInput) error {
+ if len(inputs) == 0 {
+ return nil
+ }
+
+ tx, err := r.client.Tx(ctx)
+ if err != nil {
+ return err
+ }
+ defer func() { _ = tx.Rollback() }()
+
+ for _, input := range inputs {
+ // Use upsert (ON CONFLICT DO UPDATE)
+ err := tx.UserAttributeValue.Create().
+ SetUserID(userID).
+ SetAttributeID(input.AttributeID).
+ SetValue(input.Value).
+ OnConflictColumns(userattributevalue.FieldUserID, userattributevalue.FieldAttributeID).
+ UpdateValue().
+ UpdateUpdatedAt().
+ Exec(ctx)
+ if err != nil {
+ return err
+ }
+ }
+
+ return tx.Commit()
+}
+
+func (r *userAttributeValueRepository) DeleteByAttributeID(ctx context.Context, attributeID int64) error {
+ client := clientFromContext(ctx, r.client)
+
+ _, err := client.UserAttributeValue.Delete().
+ Where(userattributevalue.AttributeIDEQ(attributeID)).
+ Exec(ctx)
+ return err
+}
+
+func (r *userAttributeValueRepository) DeleteByUserID(ctx context.Context, userID int64) error {
+ client := clientFromContext(ctx, r.client)
+
+ _, err := client.UserAttributeValue.Delete().
+ Where(userattributevalue.UserIDEQ(userID)).
+ Exec(ctx)
+ return err
+}
+
+// Helper functions for entity to service conversion
+func defEntityToService(e *dbent.UserAttributeDefinition) *service.UserAttributeDefinition {
+ if e == nil {
+ return nil
+ }
+ return &service.UserAttributeDefinition{
+ ID: e.ID,
+ Key: e.Key,
+ Name: e.Name,
+ Description: e.Description,
+ Type: service.UserAttributeType(e.Type),
+ Options: toServiceOptions(e.Options),
+ Required: e.Required,
+ Validation: toServiceValidation(e.Validation),
+ Placeholder: e.Placeholder,
+ DisplayOrder: e.DisplayOrder,
+ Enabled: e.Enabled,
+ CreatedAt: e.CreatedAt,
+ UpdatedAt: e.UpdatedAt,
+ }
+}
+
+// Type conversion helpers (map types <-> service types)
+func toEntOptions(opts []service.UserAttributeOption) []map[string]any {
+ if opts == nil {
+ return []map[string]any{}
+ }
+ result := make([]map[string]any, len(opts))
+ for i, o := range opts {
+ result[i] = map[string]any{"value": o.Value, "label": o.Label}
+ }
+ return result
+}
+
+func toServiceOptions(opts []map[string]any) []service.UserAttributeOption {
+ if opts == nil {
+ return []service.UserAttributeOption{}
+ }
+ result := make([]service.UserAttributeOption, len(opts))
+ for i, o := range opts {
+ result[i] = service.UserAttributeOption{
+ Value: getString(o, "value"),
+ Label: getString(o, "label"),
+ }
+ }
+ return result
+}
+
+func toEntValidation(v service.UserAttributeValidation) map[string]any {
+ result := map[string]any{}
+ if v.MinLength != nil {
+ result["min_length"] = *v.MinLength
+ }
+ if v.MaxLength != nil {
+ result["max_length"] = *v.MaxLength
+ }
+ if v.Min != nil {
+ result["min"] = *v.Min
+ }
+ if v.Max != nil {
+ result["max"] = *v.Max
+ }
+ if v.Pattern != nil {
+ result["pattern"] = *v.Pattern
+ }
+ if v.Message != nil {
+ result["message"] = *v.Message
+ }
+ return result
+}
+
+func toServiceValidation(v map[string]any) service.UserAttributeValidation {
+ result := service.UserAttributeValidation{}
+ if val := getInt(v, "min_length"); val != nil {
+ result.MinLength = val
+ }
+ if val := getInt(v, "max_length"); val != nil {
+ result.MaxLength = val
+ }
+ if val := getInt(v, "min"); val != nil {
+ result.Min = val
+ }
+ if val := getInt(v, "max"); val != nil {
+ result.Max = val
+ }
+ if val := getStringPtr(v, "pattern"); val != nil {
+ result.Pattern = val
+ }
+ if val := getStringPtr(v, "message"); val != nil {
+ result.Message = val
+ }
+ return result
+}
+
+// Helper functions for type conversion
+func getString(m map[string]any, key string) string {
+ if v, ok := m[key]; ok {
+ if s, ok := v.(string); ok {
+ return s
+ }
+ }
+ return ""
+}
+
+func getStringPtr(m map[string]any, key string) *string {
+ if v, ok := m[key]; ok {
+ if s, ok := v.(string); ok {
+ return &s
+ }
+ }
+ return nil
+}
+
+func getInt(m map[string]any, key string) *int {
+ if v, ok := m[key]; ok {
+ switch n := v.(type) {
+ case int:
+ return &n
+ case int64:
+ i := int(n)
+ return &i
+ case float64:
+ i := int(n)
+ return &i
+ }
+ }
+ return nil
+}
diff --git a/backend/internal/repository/wire.go b/backend/internal/repository/wire.go
index 0d579b23..c1852364 100644
--- a/backend/internal/repository/wire.go
+++ b/backend/internal/repository/wire.go
@@ -36,6 +36,8 @@ var ProviderSet = wire.NewSet(
NewUsageLogRepository,
NewSettingRepository,
NewUserSubscriptionRepository,
+ NewUserAttributeDefinitionRepository,
+ NewUserAttributeValueRepository,
// Cache implementations
NewGatewayCache,
diff --git a/backend/internal/server/routes/admin.go b/backend/internal/server/routes/admin.go
index 604d14df..74bc7469 100644
--- a/backend/internal/server/routes/admin.go
+++ b/backend/internal/server/routes/admin.go
@@ -54,6 +54,9 @@ func RegisterAdminRoutes(
// 使用记录管理
registerUsageRoutes(admin, h)
+
+ // 用户属性管理
+ registerUserAttributeRoutes(admin, h)
}
}
@@ -82,6 +85,10 @@ func registerUserManagementRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
users.POST("/:id/balance", h.Admin.User.UpdateBalance)
users.GET("/:id/api-keys", h.Admin.User.GetUserAPIKeys)
users.GET("/:id/usage", h.Admin.User.GetUserUsage)
+
+ // User attribute values
+ users.GET("/:id/attributes", h.Admin.UserAttribute.GetUserAttributes)
+ users.PUT("/:id/attributes", h.Admin.UserAttribute.UpdateUserAttributes)
}
}
@@ -242,3 +249,15 @@ func registerUsageRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
usage.GET("/search-api-keys", h.Admin.Usage.SearchApiKeys)
}
}
+
+func registerUserAttributeRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
+ attrs := admin.Group("/user-attributes")
+ {
+ attrs.GET("", h.Admin.UserAttribute.ListDefinitions)
+ attrs.POST("", h.Admin.UserAttribute.CreateDefinition)
+ attrs.POST("/batch", h.Admin.UserAttribute.GetBatchUserAttributes)
+ attrs.PUT("/reorder", h.Admin.UserAttribute.ReorderDefinitions)
+ attrs.PUT("/:id", h.Admin.UserAttribute.UpdateDefinition)
+ attrs.DELETE("/:id", h.Admin.UserAttribute.DeleteDefinition)
+ }
+}
diff --git a/backend/internal/service/user_attribute.go b/backend/internal/service/user_attribute.go
new file mode 100644
index 00000000..0637102e
--- /dev/null
+++ b/backend/internal/service/user_attribute.go
@@ -0,0 +1,125 @@
+package service
+
+import (
+ "context"
+ "time"
+
+ infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
+)
+
+// Error definitions for user attribute operations
+var (
+ ErrAttributeDefinitionNotFound = infraerrors.NotFound("ATTRIBUTE_DEFINITION_NOT_FOUND", "attribute definition not found")
+ ErrAttributeKeyExists = infraerrors.Conflict("ATTRIBUTE_KEY_EXISTS", "attribute key already exists")
+ ErrInvalidAttributeType = infraerrors.BadRequest("INVALID_ATTRIBUTE_TYPE", "invalid attribute type")
+ ErrAttributeValidationFailed = infraerrors.BadRequest("ATTRIBUTE_VALIDATION_FAILED", "attribute value validation failed")
+)
+
+// UserAttributeType represents supported attribute types
+type UserAttributeType string
+
+const (
+ AttributeTypeText UserAttributeType = "text"
+ AttributeTypeTextarea UserAttributeType = "textarea"
+ AttributeTypeNumber UserAttributeType = "number"
+ AttributeTypeEmail UserAttributeType = "email"
+ AttributeTypeURL UserAttributeType = "url"
+ AttributeTypeDate UserAttributeType = "date"
+ AttributeTypeSelect UserAttributeType = "select"
+ AttributeTypeMultiSelect UserAttributeType = "multi_select"
+)
+
+// UserAttributeOption represents a select option for select/multi_select types
+type UserAttributeOption struct {
+ Value string `json:"value"`
+ Label string `json:"label"`
+}
+
+// UserAttributeValidation represents validation rules for an attribute
+type UserAttributeValidation struct {
+ MinLength *int `json:"min_length,omitempty"`
+ MaxLength *int `json:"max_length,omitempty"`
+ Min *int `json:"min,omitempty"`
+ Max *int `json:"max,omitempty"`
+ Pattern *string `json:"pattern,omitempty"`
+ Message *string `json:"message,omitempty"`
+}
+
+// UserAttributeDefinition represents a custom attribute definition
+type UserAttributeDefinition struct {
+ ID int64
+ Key string
+ Name string
+ Description string
+ Type UserAttributeType
+ Options []UserAttributeOption
+ Required bool
+ Validation UserAttributeValidation
+ Placeholder string
+ DisplayOrder int
+ Enabled bool
+ CreatedAt time.Time
+ UpdatedAt time.Time
+}
+
+// UserAttributeValue represents a user's attribute value
+type UserAttributeValue struct {
+ ID int64
+ UserID int64
+ AttributeID int64
+ Value string
+ CreatedAt time.Time
+ UpdatedAt time.Time
+}
+
+// CreateAttributeDefinitionInput for creating new definition
+type CreateAttributeDefinitionInput struct {
+ Key string
+ Name string
+ Description string
+ Type UserAttributeType
+ Options []UserAttributeOption
+ Required bool
+ Validation UserAttributeValidation
+ Placeholder string
+ Enabled bool
+}
+
+// UpdateAttributeDefinitionInput for updating definition
+type UpdateAttributeDefinitionInput struct {
+ Name *string
+ Description *string
+ Type *UserAttributeType
+ Options *[]UserAttributeOption
+ Required *bool
+ Validation *UserAttributeValidation
+ Placeholder *string
+ Enabled *bool
+}
+
+// UpdateUserAttributeInput for updating a single attribute value
+type UpdateUserAttributeInput struct {
+ AttributeID int64
+ Value string
+}
+
+// UserAttributeDefinitionRepository interface for attribute definition persistence
+type UserAttributeDefinitionRepository interface {
+ Create(ctx context.Context, def *UserAttributeDefinition) error
+ GetByID(ctx context.Context, id int64) (*UserAttributeDefinition, error)
+ GetByKey(ctx context.Context, key string) (*UserAttributeDefinition, error)
+ Update(ctx context.Context, def *UserAttributeDefinition) error
+ Delete(ctx context.Context, id int64) error
+ List(ctx context.Context, enabledOnly bool) ([]UserAttributeDefinition, error)
+ UpdateDisplayOrders(ctx context.Context, orders map[int64]int) error
+ ExistsByKey(ctx context.Context, key string) (bool, error)
+}
+
+// UserAttributeValueRepository interface for user attribute value persistence
+type UserAttributeValueRepository interface {
+ GetByUserID(ctx context.Context, userID int64) ([]UserAttributeValue, error)
+ GetByUserIDs(ctx context.Context, userIDs []int64) ([]UserAttributeValue, error)
+ UpsertBatch(ctx context.Context, userID int64, values []UpdateUserAttributeInput) error
+ DeleteByAttributeID(ctx context.Context, attributeID int64) error
+ DeleteByUserID(ctx context.Context, userID int64) error
+}
diff --git a/backend/internal/service/user_attribute_service.go b/backend/internal/service/user_attribute_service.go
new file mode 100644
index 00000000..c27e29d0
--- /dev/null
+++ b/backend/internal/service/user_attribute_service.go
@@ -0,0 +1,295 @@
+package service
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
+)
+
+// UserAttributeService handles attribute management
+type UserAttributeService struct {
+ defRepo UserAttributeDefinitionRepository
+ valueRepo UserAttributeValueRepository
+}
+
+// NewUserAttributeService creates a new service instance
+func NewUserAttributeService(
+ defRepo UserAttributeDefinitionRepository,
+ valueRepo UserAttributeValueRepository,
+) *UserAttributeService {
+ return &UserAttributeService{
+ defRepo: defRepo,
+ valueRepo: valueRepo,
+ }
+}
+
+// CreateDefinition creates a new attribute definition
+func (s *UserAttributeService) CreateDefinition(ctx context.Context, input CreateAttributeDefinitionInput) (*UserAttributeDefinition, error) {
+ // Validate type
+ if !isValidAttributeType(input.Type) {
+ return nil, ErrInvalidAttributeType
+ }
+
+ // Check if key exists
+ exists, err := s.defRepo.ExistsByKey(ctx, input.Key)
+ if err != nil {
+ return nil, fmt.Errorf("check key exists: %w", err)
+ }
+ if exists {
+ return nil, ErrAttributeKeyExists
+ }
+
+ def := &UserAttributeDefinition{
+ Key: input.Key,
+ Name: input.Name,
+ Description: input.Description,
+ Type: input.Type,
+ Options: input.Options,
+ Required: input.Required,
+ Validation: input.Validation,
+ Placeholder: input.Placeholder,
+ Enabled: input.Enabled,
+ }
+
+ if err := s.defRepo.Create(ctx, def); err != nil {
+ return nil, fmt.Errorf("create definition: %w", err)
+ }
+
+ return def, nil
+}
+
+// GetDefinition retrieves a definition by ID
+func (s *UserAttributeService) GetDefinition(ctx context.Context, id int64) (*UserAttributeDefinition, error) {
+ return s.defRepo.GetByID(ctx, id)
+}
+
+// ListDefinitions lists all definitions
+func (s *UserAttributeService) ListDefinitions(ctx context.Context, enabledOnly bool) ([]UserAttributeDefinition, error) {
+ return s.defRepo.List(ctx, enabledOnly)
+}
+
+// UpdateDefinition updates an existing definition
+func (s *UserAttributeService) UpdateDefinition(ctx context.Context, id int64, input UpdateAttributeDefinitionInput) (*UserAttributeDefinition, error) {
+ def, err := s.defRepo.GetByID(ctx, id)
+ if err != nil {
+ return nil, err
+ }
+
+ if input.Name != nil {
+ def.Name = *input.Name
+ }
+ if input.Description != nil {
+ def.Description = *input.Description
+ }
+ if input.Type != nil {
+ if !isValidAttributeType(*input.Type) {
+ return nil, ErrInvalidAttributeType
+ }
+ def.Type = *input.Type
+ }
+ if input.Options != nil {
+ def.Options = *input.Options
+ }
+ if input.Required != nil {
+ def.Required = *input.Required
+ }
+ if input.Validation != nil {
+ def.Validation = *input.Validation
+ }
+ if input.Placeholder != nil {
+ def.Placeholder = *input.Placeholder
+ }
+ if input.Enabled != nil {
+ def.Enabled = *input.Enabled
+ }
+
+ if err := s.defRepo.Update(ctx, def); err != nil {
+ return nil, fmt.Errorf("update definition: %w", err)
+ }
+
+ return def, nil
+}
+
+// DeleteDefinition soft-deletes a definition and hard-deletes associated values
+func (s *UserAttributeService) DeleteDefinition(ctx context.Context, id int64) error {
+ // Check if definition exists
+ _, err := s.defRepo.GetByID(ctx, id)
+ if err != nil {
+ return err
+ }
+
+ // First delete all values (hard delete)
+ if err := s.valueRepo.DeleteByAttributeID(ctx, id); err != nil {
+ return fmt.Errorf("delete values: %w", err)
+ }
+
+ // Then soft-delete the definition
+ if err := s.defRepo.Delete(ctx, id); err != nil {
+ return fmt.Errorf("delete definition: %w", err)
+ }
+
+ return nil
+}
+
+// ReorderDefinitions updates display order for multiple definitions
+func (s *UserAttributeService) ReorderDefinitions(ctx context.Context, orders map[int64]int) error {
+ return s.defRepo.UpdateDisplayOrders(ctx, orders)
+}
+
+// GetUserAttributes retrieves all attribute values for a user
+func (s *UserAttributeService) GetUserAttributes(ctx context.Context, userID int64) ([]UserAttributeValue, error) {
+ return s.valueRepo.GetByUserID(ctx, userID)
+}
+
+// GetBatchUserAttributes retrieves attribute values for multiple users
+// Returns a map of userID -> map of attributeID -> value
+func (s *UserAttributeService) GetBatchUserAttributes(ctx context.Context, userIDs []int64) (map[int64]map[int64]string, error) {
+ values, err := s.valueRepo.GetByUserIDs(ctx, userIDs)
+ if err != nil {
+ return nil, err
+ }
+
+ result := make(map[int64]map[int64]string)
+ for _, v := range values {
+ if result[v.UserID] == nil {
+ result[v.UserID] = make(map[int64]string)
+ }
+ result[v.UserID][v.AttributeID] = v.Value
+ }
+
+ return result, nil
+}
+
+// UpdateUserAttributes batch updates attribute values for a user
+func (s *UserAttributeService) UpdateUserAttributes(ctx context.Context, userID int64, inputs []UpdateUserAttributeInput) error {
+ // Validate all values before updating
+ defs, err := s.defRepo.List(ctx, true)
+ if err != nil {
+ return fmt.Errorf("list definitions: %w", err)
+ }
+
+ defMap := make(map[int64]*UserAttributeDefinition, len(defs))
+ for i := range defs {
+ defMap[defs[i].ID] = &defs[i]
+ }
+
+ for _, input := range inputs {
+ def, ok := defMap[input.AttributeID]
+ if !ok {
+ return ErrAttributeDefinitionNotFound
+ }
+
+ if err := s.validateValue(def, input.Value); err != nil {
+ return err
+ }
+ }
+
+ return s.valueRepo.UpsertBatch(ctx, userID, inputs)
+}
+
+// validateValue validates a value against its definition
+func (s *UserAttributeService) validateValue(def *UserAttributeDefinition, value string) error {
+ // Skip validation for empty non-required fields
+ if value == "" && !def.Required {
+ return nil
+ }
+
+ // Required check
+ if def.Required && value == "" {
+ return validationError(fmt.Sprintf("%s is required", def.Name))
+ }
+
+ v := def.Validation
+
+ // String length validation
+ if v.MinLength != nil && len(value) < *v.MinLength {
+ return validationError(fmt.Sprintf("%s must be at least %d characters", def.Name, *v.MinLength))
+ }
+ if v.MaxLength != nil && len(value) > *v.MaxLength {
+ return validationError(fmt.Sprintf("%s must be at most %d characters", def.Name, *v.MaxLength))
+ }
+
+ // Number validation
+ if def.Type == AttributeTypeNumber && value != "" {
+ num, err := strconv.Atoi(value)
+ if err != nil {
+ return validationError(fmt.Sprintf("%s must be a number", def.Name))
+ }
+ if v.Min != nil && num < *v.Min {
+ return validationError(fmt.Sprintf("%s must be at least %d", def.Name, *v.Min))
+ }
+ if v.Max != nil && num > *v.Max {
+ return validationError(fmt.Sprintf("%s must be at most %d", def.Name, *v.Max))
+ }
+ }
+
+ // Pattern validation
+ if v.Pattern != nil && *v.Pattern != "" && value != "" {
+ re, err := regexp.Compile(*v.Pattern)
+ if err == nil && !re.MatchString(value) {
+ msg := def.Name + " format is invalid"
+ if v.Message != nil && *v.Message != "" {
+ msg = *v.Message
+ }
+ return validationError(msg)
+ }
+ }
+
+ // Select validation
+ if def.Type == AttributeTypeSelect && value != "" {
+ found := false
+ for _, opt := range def.Options {
+ if opt.Value == value {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return validationError(fmt.Sprintf("%s: invalid option", def.Name))
+ }
+ }
+
+ // Multi-select validation (stored as JSON array)
+ if def.Type == AttributeTypeMultiSelect && value != "" {
+ var values []string
+ if err := json.Unmarshal([]byte(value), &values); err != nil {
+ // Try comma-separated fallback
+ values = strings.Split(value, ",")
+ }
+ for _, val := range values {
+ val = strings.TrimSpace(val)
+ found := false
+ for _, opt := range def.Options {
+ if opt.Value == val {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return validationError(fmt.Sprintf("%s: invalid option %s", def.Name, val))
+ }
+ }
+ }
+
+ return nil
+}
+
+// validationError creates a validation error with a custom message
+func validationError(msg string) error {
+ return infraerrors.BadRequest("ATTRIBUTE_VALIDATION_FAILED", msg)
+}
+
+func isValidAttributeType(t UserAttributeType) bool {
+ switch t {
+ case AttributeTypeText, AttributeTypeTextarea, AttributeTypeNumber,
+ AttributeTypeEmail, AttributeTypeURL, AttributeTypeDate,
+ AttributeTypeSelect, AttributeTypeMultiSelect:
+ return true
+ }
+ return false
+}
diff --git a/backend/internal/service/wire.go b/backend/internal/service/wire.go
index 9920a3ef..7971f041 100644
--- a/backend/internal/service/wire.go
+++ b/backend/internal/service/wire.go
@@ -125,4 +125,5 @@ var ProviderSet = wire.NewSet(
ProvideTimingWheelService,
ProvideDeferredService,
ProvideAntigravityQuotaRefresher,
+ NewUserAttributeService,
)
diff --git a/backend/migrations/018_user_attributes.sql b/backend/migrations/018_user_attributes.sql
new file mode 100644
index 00000000..d2dad80d
--- /dev/null
+++ b/backend/migrations/018_user_attributes.sql
@@ -0,0 +1,48 @@
+-- Add user attribute definitions and values tables for custom user attributes.
+
+-- User Attribute Definitions table (with soft delete support)
+CREATE TABLE IF NOT EXISTS user_attribute_definitions (
+ id BIGSERIAL PRIMARY KEY,
+ key VARCHAR(100) NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ description TEXT DEFAULT '',
+ type VARCHAR(20) NOT NULL,
+ options JSONB DEFAULT '[]'::jsonb,
+ required BOOLEAN NOT NULL DEFAULT FALSE,
+ validation JSONB DEFAULT '{}'::jsonb,
+ placeholder VARCHAR(255) DEFAULT '',
+ display_order INT NOT NULL DEFAULT 0,
+ enabled BOOLEAN NOT NULL DEFAULT TRUE,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ deleted_at TIMESTAMPTZ
+);
+
+-- Partial unique index for key (only for non-deleted records)
+-- Allows reusing keys after soft delete
+CREATE UNIQUE INDEX IF NOT EXISTS idx_user_attribute_definitions_key_unique
+ ON user_attribute_definitions(key) WHERE deleted_at IS NULL;
+
+CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_enabled
+ ON user_attribute_definitions(enabled);
+CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_display_order
+ ON user_attribute_definitions(display_order);
+CREATE INDEX IF NOT EXISTS idx_user_attribute_definitions_deleted_at
+ ON user_attribute_definitions(deleted_at);
+
+-- User Attribute Values table (hard delete only, no deleted_at)
+CREATE TABLE IF NOT EXISTS user_attribute_values (
+ id BIGSERIAL PRIMARY KEY,
+ user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ attribute_id BIGINT NOT NULL REFERENCES user_attribute_definitions(id) ON DELETE CASCADE,
+ value TEXT DEFAULT '',
+ created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
+
+ UNIQUE(user_id, attribute_id)
+);
+
+CREATE INDEX IF NOT EXISTS idx_user_attribute_values_user_id
+ ON user_attribute_values(user_id);
+CREATE INDEX IF NOT EXISTS idx_user_attribute_values_attribute_id
+ ON user_attribute_values(attribute_id);
From f44cf642bc7f7165804d91ad08e0440ce1b6f94d Mon Sep 17 00:00:00 2001
From: Edric Li
Date: Thu, 1 Jan 2026 18:59:06 +0800
Subject: [PATCH 41/51] feat(frontend): add user attributes management UI
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add Vue components and API client for managing user custom attributes.
- Add userAttributes API client with CRUD operations
- Add UserAttributeForm component for displaying/editing attribute values
- Add UserAttributesConfigModal for attribute definition management
- Support all attribute types: text, textarea, number, email, url,
date, select, multi_select
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5
---
frontend/src/api/admin/index.ts | 7 +-
frontend/src/api/admin/userAttributes.ts | 131 ++++++
.../src/components/user/UserAttributeForm.vue | 207 +++++++++
.../user/UserAttributesConfigModal.vue | 404 ++++++++++++++++++
4 files changed, 747 insertions(+), 2 deletions(-)
create mode 100644 frontend/src/api/admin/userAttributes.ts
create mode 100644 frontend/src/components/user/UserAttributeForm.vue
create mode 100644 frontend/src/components/user/UserAttributesConfigModal.vue
diff --git a/frontend/src/api/admin/index.ts b/frontend/src/api/admin/index.ts
index 7c98b74e..ea12f6d2 100644
--- a/frontend/src/api/admin/index.ts
+++ b/frontend/src/api/admin/index.ts
@@ -15,6 +15,7 @@ import subscriptionsAPI from './subscriptions'
import usageAPI from './usage'
import geminiAPI from './gemini'
import antigravityAPI from './antigravity'
+import userAttributesAPI from './userAttributes'
/**
* Unified admin API object for convenient access
@@ -31,7 +32,8 @@ export const adminAPI = {
subscriptions: subscriptionsAPI,
usage: usageAPI,
gemini: geminiAPI,
- antigravity: antigravityAPI
+ antigravity: antigravityAPI,
+ userAttributes: userAttributesAPI
}
export {
@@ -46,7 +48,8 @@ export {
subscriptionsAPI,
usageAPI,
geminiAPI,
- antigravityAPI
+ antigravityAPI,
+ userAttributesAPI
}
export default adminAPI
diff --git a/frontend/src/api/admin/userAttributes.ts b/frontend/src/api/admin/userAttributes.ts
new file mode 100644
index 00000000..304aa828
--- /dev/null
+++ b/frontend/src/api/admin/userAttributes.ts
@@ -0,0 +1,131 @@
+/**
+ * Admin User Attributes API endpoints
+ * Handles user custom attribute definitions and values
+ */
+
+import { apiClient } from '../client'
+import type {
+ UserAttributeDefinition,
+ UserAttributeValue,
+ CreateUserAttributeRequest,
+ UpdateUserAttributeRequest,
+ UserAttributeValuesMap
+} from '@/types'
+
+/**
+ * Get all attribute definitions
+ */
+export async function listDefinitions(): Promise {
+ const { data } = await apiClient.get('/admin/user-attributes')
+ return data
+}
+
+/**
+ * Get enabled attribute definitions only
+ */
+export async function listEnabledDefinitions(): Promise {
+ const { data } = await apiClient.get('/admin/user-attributes', {
+ params: { enabled: true }
+ })
+ return data
+}
+
+/**
+ * Create a new attribute definition
+ */
+export async function createDefinition(
+ request: CreateUserAttributeRequest
+): Promise {
+ const { data } = await apiClient.post('/admin/user-attributes', request)
+ return data
+}
+
+/**
+ * Update an attribute definition
+ */
+export async function updateDefinition(
+ id: number,
+ request: UpdateUserAttributeRequest
+): Promise {
+ const { data } = await apiClient.put(
+ `/admin/user-attributes/${id}`,
+ request
+ )
+ return data
+}
+
+/**
+ * Delete an attribute definition
+ */
+export async function deleteDefinition(id: number): Promise<{ message: string }> {
+ const { data } = await apiClient.delete<{ message: string }>(`/admin/user-attributes/${id}`)
+ return data
+}
+
+/**
+ * Reorder attribute definitions
+ */
+export async function reorderDefinitions(ids: number[]): Promise<{ message: string }> {
+ const { data } = await apiClient.put<{ message: string }>('/admin/user-attributes/reorder', {
+ ids
+ })
+ return data
+}
+
+/**
+ * Get user's attribute values
+ */
+export async function getUserAttributeValues(userId: number): Promise {
+ const { data } = await apiClient.get(
+ `/admin/users/${userId}/attributes`
+ )
+ return data
+}
+
+/**
+ * Update user's attribute values (batch)
+ */
+export async function updateUserAttributeValues(
+ userId: number,
+ values: UserAttributeValuesMap
+): Promise<{ message: string }> {
+ const { data } = await apiClient.put<{ message: string }>(
+ `/admin/users/${userId}/attributes`,
+ { values }
+ )
+ return data
+}
+
+/**
+ * Batch response type
+ */
+export interface BatchUserAttributesResponse {
+ attributes: Record>
+}
+
+/**
+ * Get attribute values for multiple users
+ */
+export async function getBatchUserAttributes(
+ userIds: number[]
+): Promise {
+ const { data } = await apiClient.post(
+ '/admin/user-attributes/batch',
+ { user_ids: userIds }
+ )
+ return data
+}
+
+export const userAttributesAPI = {
+ listDefinitions,
+ listEnabledDefinitions,
+ createDefinition,
+ updateDefinition,
+ deleteDefinition,
+ reorderDefinitions,
+ getUserAttributeValues,
+ updateUserAttributeValues,
+ getBatchUserAttributes
+}
+
+export default userAttributesAPI
diff --git a/frontend/src/components/user/UserAttributeForm.vue b/frontend/src/components/user/UserAttributeForm.vue
new file mode 100644
index 00000000..68807c5d
--- /dev/null
+++ b/frontend/src/components/user/UserAttributeForm.vue
@@ -0,0 +1,207 @@
+
+
+
+
+
+
+
+
diff --git a/frontend/src/components/user/UserAttributesConfigModal.vue b/frontend/src/components/user/UserAttributesConfigModal.vue
new file mode 100644
index 00000000..8da30898
--- /dev/null
+++ b/frontend/src/components/user/UserAttributesConfigModal.vue
@@ -0,0 +1,404 @@
+
+
+
+
+
+
+ {{ t('admin.users.attributes.description') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ t('admin.users.attributes.noAttributes') }}
+
+
+ {{ t('admin.users.attributes.noAttributesHint') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ attr.name }}
+
+ {{ attr.key }}
+
+
+ {{ t('admin.users.attributes.required') }}
+
+
+ {{ t('common.disabled') }}
+
+
+
+ {{ t(`admin.users.attributes.types.${attr.type}`) }}
+ {{ attr.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 404bf0f8d2a345775a2043d14143f0a53fba7c7d Mon Sep 17 00:00:00 2001
From: Edric Li
Date: Thu, 1 Jan 2026 18:59:38 +0800
Subject: [PATCH 42/51] refactor: migrate wechat to user attributes and enhance
users list
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Migrate the hardcoded wechat field to the new extensible user
attributes system and improve the users management UI.
Migration:
- Add migration 019 to move wechat data to user_attribute_values
- Remove wechat field from User entity, DTOs, and API contracts
- Clean up wechat-related code from backend and frontend
UsersView enhancements:
- Add text labels to action buttons (Filter Settings, Column Settings,
Attributes Config) for better UX
- Change status column to show colored dot + Chinese text instead of
English text
- Add dynamic attribute columns support with batch loading
- Add column visibility settings with localStorage persistence
- Add filter settings modal for search and filter preferences
- Update i18n translations
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5
---
backend/ent/schema/user.go | 5 +-
backend/ent/user.go | 33 +-
backend/ent/user/user.go | 42 +-
backend/ent/user/where.go | 93 +--
backend/ent/user_create.go | 102 +--
backend/ent/user_query.go | 76 +-
backend/ent/user_update.go | 207 ++++-
.../internal/handler/admin/usage_handler.go | 2 +-
.../internal/handler/admin/user_handler.go | 44 +-
backend/internal/handler/dto/mappers.go | 1 -
backend/internal/handler/dto/types.go | 1 -
backend/internal/handler/user_handler.go | 2 -
backend/internal/repository/api_key_repo.go | 1 -
.../repository/fixtures_integration_test.go | 1 -
.../migrations_schema_integration_test.go | 1 -
backend/internal/repository/user_repo.go | 86 +-
.../repository/user_repo_integration_test.go | 14 -
backend/internal/server/api_contract_test.go | 1 -
backend/internal/service/admin_service.go | 12 +-
.../service/admin_service_create_user_test.go | 2 -
backend/internal/service/user.go | 1 -
backend/internal/service/user_service.go | 15 +-
.../019_migrate_wechat_to_attributes.sql | 83 ++
frontend/src/api/admin/users.ts | 27 +-
frontend/src/api/user.ts | 1 -
frontend/src/i18n/locales/en.ts | 67 +-
frontend/src/i18n/locales/zh.ts | 69 +-
frontend/src/types/index.ts | 75 +-
frontend/src/views/admin/UsersView.vue | 749 +++++++++++++++---
frontend/src/views/user/ProfileView.vue | 39 +-
30 files changed, 1390 insertions(+), 462 deletions(-)
create mode 100644 backend/migrations/019_migrate_wechat_to_attributes.sql
diff --git a/backend/ent/schema/user.go b/backend/ent/schema/user.go
index c1f742d1..f29b6123 100644
--- a/backend/ent/schema/user.go
+++ b/backend/ent/schema/user.go
@@ -57,9 +57,7 @@ func (User) Fields() []ent.Field {
field.String("username").
MaxLen(100).
Default(""),
- field.String("wechat").
- MaxLen(100).
- Default(""),
+ // wechat field migrated to user_attribute_values (see migration 019)
field.String("notes").
SchemaType(map[string]string{dialect.Postgres: "text"}).
Default(""),
@@ -75,6 +73,7 @@ func (User) Edges() []ent.Edge {
edge.To("allowed_groups", Group.Type).
Through("user_allowed_groups", UserAllowedGroup.Type),
edge.To("usage_logs", UsageLog.Type),
+ edge.To("attribute_values", UserAttributeValue.Type),
}
}
diff --git a/backend/ent/user.go b/backend/ent/user.go
index eda67c84..d7e1668d 100644
--- a/backend/ent/user.go
+++ b/backend/ent/user.go
@@ -37,8 +37,6 @@ type User struct {
Status string `json:"status,omitempty"`
// Username holds the value of the "username" field.
Username string `json:"username,omitempty"`
- // Wechat holds the value of the "wechat" field.
- Wechat string `json:"wechat,omitempty"`
// Notes holds the value of the "notes" field.
Notes string `json:"notes,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
@@ -61,11 +59,13 @@ type UserEdges struct {
AllowedGroups []*Group `json:"allowed_groups,omitempty"`
// UsageLogs holds the value of the usage_logs edge.
UsageLogs []*UsageLog `json:"usage_logs,omitempty"`
+ // AttributeValues holds the value of the attribute_values edge.
+ AttributeValues []*UserAttributeValue `json:"attribute_values,omitempty"`
// UserAllowedGroups holds the value of the user_allowed_groups edge.
UserAllowedGroups []*UserAllowedGroup `json:"user_allowed_groups,omitempty"`
// loadedTypes holds the information for reporting if a
// type was loaded (or requested) in eager-loading or not.
- loadedTypes [7]bool
+ loadedTypes [8]bool
}
// APIKeysOrErr returns the APIKeys value or an error if the edge
@@ -122,10 +122,19 @@ func (e UserEdges) UsageLogsOrErr() ([]*UsageLog, error) {
return nil, &NotLoadedError{edge: "usage_logs"}
}
+// AttributeValuesOrErr returns the AttributeValues value or an error if the edge
+// was not loaded in eager-loading.
+func (e UserEdges) AttributeValuesOrErr() ([]*UserAttributeValue, error) {
+ if e.loadedTypes[6] {
+ return e.AttributeValues, nil
+ }
+ return nil, &NotLoadedError{edge: "attribute_values"}
+}
+
// UserAllowedGroupsOrErr returns the UserAllowedGroups value or an error if the edge
// was not loaded in eager-loading.
func (e UserEdges) UserAllowedGroupsOrErr() ([]*UserAllowedGroup, error) {
- if e.loadedTypes[6] {
+ if e.loadedTypes[7] {
return e.UserAllowedGroups, nil
}
return nil, &NotLoadedError{edge: "user_allowed_groups"}
@@ -140,7 +149,7 @@ func (*User) scanValues(columns []string) ([]any, error) {
values[i] = new(sql.NullFloat64)
case user.FieldID, user.FieldConcurrency:
values[i] = new(sql.NullInt64)
- case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldWechat, user.FieldNotes:
+ case user.FieldEmail, user.FieldPasswordHash, user.FieldRole, user.FieldStatus, user.FieldUsername, user.FieldNotes:
values[i] = new(sql.NullString)
case user.FieldCreatedAt, user.FieldUpdatedAt, user.FieldDeletedAt:
values[i] = new(sql.NullTime)
@@ -226,12 +235,6 @@ func (_m *User) assignValues(columns []string, values []any) error {
} else if value.Valid {
_m.Username = value.String
}
- case user.FieldWechat:
- if value, ok := values[i].(*sql.NullString); !ok {
- return fmt.Errorf("unexpected type %T for field wechat", values[i])
- } else if value.Valid {
- _m.Wechat = value.String
- }
case user.FieldNotes:
if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field notes", values[i])
@@ -281,6 +284,11 @@ func (_m *User) QueryUsageLogs() *UsageLogQuery {
return NewUserClient(_m.config).QueryUsageLogs(_m)
}
+// QueryAttributeValues queries the "attribute_values" edge of the User entity.
+func (_m *User) QueryAttributeValues() *UserAttributeValueQuery {
+ return NewUserClient(_m.config).QueryAttributeValues(_m)
+}
+
// QueryUserAllowedGroups queries the "user_allowed_groups" edge of the User entity.
func (_m *User) QueryUserAllowedGroups() *UserAllowedGroupQuery {
return NewUserClient(_m.config).QueryUserAllowedGroups(_m)
@@ -341,9 +349,6 @@ func (_m *User) String() string {
builder.WriteString("username=")
builder.WriteString(_m.Username)
builder.WriteString(", ")
- builder.WriteString("wechat=")
- builder.WriteString(_m.Wechat)
- builder.WriteString(", ")
builder.WriteString("notes=")
builder.WriteString(_m.Notes)
builder.WriteByte(')')
diff --git a/backend/ent/user/user.go b/backend/ent/user/user.go
index 9ad87890..9c40ab09 100644
--- a/backend/ent/user/user.go
+++ b/backend/ent/user/user.go
@@ -35,8 +35,6 @@ const (
FieldStatus = "status"
// FieldUsername holds the string denoting the username field in the database.
FieldUsername = "username"
- // FieldWechat holds the string denoting the wechat field in the database.
- FieldWechat = "wechat"
// FieldNotes holds the string denoting the notes field in the database.
FieldNotes = "notes"
// EdgeAPIKeys holds the string denoting the api_keys edge name in mutations.
@@ -51,6 +49,8 @@ const (
EdgeAllowedGroups = "allowed_groups"
// EdgeUsageLogs holds the string denoting the usage_logs edge name in mutations.
EdgeUsageLogs = "usage_logs"
+ // EdgeAttributeValues holds the string denoting the attribute_values edge name in mutations.
+ EdgeAttributeValues = "attribute_values"
// EdgeUserAllowedGroups holds the string denoting the user_allowed_groups edge name in mutations.
EdgeUserAllowedGroups = "user_allowed_groups"
// Table holds the table name of the user in the database.
@@ -95,6 +95,13 @@ const (
UsageLogsInverseTable = "usage_logs"
// UsageLogsColumn is the table column denoting the usage_logs relation/edge.
UsageLogsColumn = "user_id"
+ // AttributeValuesTable is the table that holds the attribute_values relation/edge.
+ AttributeValuesTable = "user_attribute_values"
+ // AttributeValuesInverseTable is the table name for the UserAttributeValue entity.
+ // It exists in this package in order to avoid circular dependency with the "userattributevalue" package.
+ AttributeValuesInverseTable = "user_attribute_values"
+ // AttributeValuesColumn is the table column denoting the attribute_values relation/edge.
+ AttributeValuesColumn = "user_id"
// UserAllowedGroupsTable is the table that holds the user_allowed_groups relation/edge.
UserAllowedGroupsTable = "user_allowed_groups"
// UserAllowedGroupsInverseTable is the table name for the UserAllowedGroup entity.
@@ -117,7 +124,6 @@ var Columns = []string{
FieldConcurrency,
FieldStatus,
FieldUsername,
- FieldWechat,
FieldNotes,
}
@@ -171,10 +177,6 @@ var (
DefaultUsername string
// UsernameValidator is a validator for the "username" field. It is called by the builders before save.
UsernameValidator func(string) error
- // DefaultWechat holds the default value on creation for the "wechat" field.
- DefaultWechat string
- // WechatValidator is a validator for the "wechat" field. It is called by the builders before save.
- WechatValidator func(string) error
// DefaultNotes holds the default value on creation for the "notes" field.
DefaultNotes string
)
@@ -237,11 +239,6 @@ func ByUsername(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUsername, opts...).ToFunc()
}
-// ByWechat orders the results by the wechat field.
-func ByWechat(opts ...sql.OrderTermOption) OrderOption {
- return sql.OrderByField(FieldWechat, opts...).ToFunc()
-}
-
// ByNotes orders the results by the notes field.
func ByNotes(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldNotes, opts...).ToFunc()
@@ -331,6 +328,20 @@ func ByUsageLogs(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
}
}
+// ByAttributeValuesCount orders the results by attribute_values count.
+func ByAttributeValuesCount(opts ...sql.OrderTermOption) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborsCount(s, newAttributeValuesStep(), opts...)
+ }
+}
+
+// ByAttributeValues orders the results by attribute_values terms.
+func ByAttributeValues(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
+ return func(s *sql.Selector) {
+ sqlgraph.OrderByNeighborTerms(s, newAttributeValuesStep(), append([]sql.OrderTerm{term}, terms...)...)
+ }
+}
+
// ByUserAllowedGroupsCount orders the results by user_allowed_groups count.
func ByUserAllowedGroupsCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
@@ -386,6 +397,13 @@ func newUsageLogsStep() *sqlgraph.Step {
sqlgraph.Edge(sqlgraph.O2M, false, UsageLogsTable, UsageLogsColumn),
)
}
+func newAttributeValuesStep() *sqlgraph.Step {
+ return sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.To(AttributeValuesInverseTable, FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, AttributeValuesTable, AttributeValuesColumn),
+ )
+}
func newUserAllowedGroupsStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
diff --git a/backend/ent/user/where.go b/backend/ent/user/where.go
index 81959cf4..c3db075e 100644
--- a/backend/ent/user/where.go
+++ b/backend/ent/user/where.go
@@ -105,11 +105,6 @@ func Username(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldUsername, v))
}
-// Wechat applies equality check predicate on the "wechat" field. It's identical to WechatEQ.
-func Wechat(v string) predicate.User {
- return predicate.User(sql.FieldEQ(FieldWechat, v))
-}
-
// Notes applies equality check predicate on the "notes" field. It's identical to NotesEQ.
func Notes(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldNotes, v))
@@ -650,71 +645,6 @@ func UsernameContainsFold(v string) predicate.User {
return predicate.User(sql.FieldContainsFold(FieldUsername, v))
}
-// WechatEQ applies the EQ predicate on the "wechat" field.
-func WechatEQ(v string) predicate.User {
- return predicate.User(sql.FieldEQ(FieldWechat, v))
-}
-
-// WechatNEQ applies the NEQ predicate on the "wechat" field.
-func WechatNEQ(v string) predicate.User {
- return predicate.User(sql.FieldNEQ(FieldWechat, v))
-}
-
-// WechatIn applies the In predicate on the "wechat" field.
-func WechatIn(vs ...string) predicate.User {
- return predicate.User(sql.FieldIn(FieldWechat, vs...))
-}
-
-// WechatNotIn applies the NotIn predicate on the "wechat" field.
-func WechatNotIn(vs ...string) predicate.User {
- return predicate.User(sql.FieldNotIn(FieldWechat, vs...))
-}
-
-// WechatGT applies the GT predicate on the "wechat" field.
-func WechatGT(v string) predicate.User {
- return predicate.User(sql.FieldGT(FieldWechat, v))
-}
-
-// WechatGTE applies the GTE predicate on the "wechat" field.
-func WechatGTE(v string) predicate.User {
- return predicate.User(sql.FieldGTE(FieldWechat, v))
-}
-
-// WechatLT applies the LT predicate on the "wechat" field.
-func WechatLT(v string) predicate.User {
- return predicate.User(sql.FieldLT(FieldWechat, v))
-}
-
-// WechatLTE applies the LTE predicate on the "wechat" field.
-func WechatLTE(v string) predicate.User {
- return predicate.User(sql.FieldLTE(FieldWechat, v))
-}
-
-// WechatContains applies the Contains predicate on the "wechat" field.
-func WechatContains(v string) predicate.User {
- return predicate.User(sql.FieldContains(FieldWechat, v))
-}
-
-// WechatHasPrefix applies the HasPrefix predicate on the "wechat" field.
-func WechatHasPrefix(v string) predicate.User {
- return predicate.User(sql.FieldHasPrefix(FieldWechat, v))
-}
-
-// WechatHasSuffix applies the HasSuffix predicate on the "wechat" field.
-func WechatHasSuffix(v string) predicate.User {
- return predicate.User(sql.FieldHasSuffix(FieldWechat, v))
-}
-
-// WechatEqualFold applies the EqualFold predicate on the "wechat" field.
-func WechatEqualFold(v string) predicate.User {
- return predicate.User(sql.FieldEqualFold(FieldWechat, v))
-}
-
-// WechatContainsFold applies the ContainsFold predicate on the "wechat" field.
-func WechatContainsFold(v string) predicate.User {
- return predicate.User(sql.FieldContainsFold(FieldWechat, v))
-}
-
// NotesEQ applies the EQ predicate on the "notes" field.
func NotesEQ(v string) predicate.User {
return predicate.User(sql.FieldEQ(FieldNotes, v))
@@ -918,6 +848,29 @@ func HasUsageLogsWith(preds ...predicate.UsageLog) predicate.User {
})
}
+// HasAttributeValues applies the HasEdge predicate on the "attribute_values" edge.
+func HasAttributeValues() predicate.User {
+ return predicate.User(func(s *sql.Selector) {
+ step := sqlgraph.NewStep(
+ sqlgraph.From(Table, FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, AttributeValuesTable, AttributeValuesColumn),
+ )
+ sqlgraph.HasNeighbors(s, step)
+ })
+}
+
+// HasAttributeValuesWith applies the HasEdge predicate on the "attribute_values" edge with a given conditions (other predicates).
+func HasAttributeValuesWith(preds ...predicate.UserAttributeValue) predicate.User {
+ return predicate.User(func(s *sql.Selector) {
+ step := newAttributeValuesStep()
+ sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
+ for _, p := range preds {
+ p(s)
+ }
+ })
+ })
+}
+
// HasUserAllowedGroups applies the HasEdge predicate on the "user_allowed_groups" edge.
func HasUserAllowedGroups() predicate.User {
return predicate.User(func(s *sql.Selector) {
diff --git a/backend/ent/user_create.go b/backend/ent/user_create.go
index 51bdc493..6313db5f 100644
--- a/backend/ent/user_create.go
+++ b/backend/ent/user_create.go
@@ -16,6 +16,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -151,20 +152,6 @@ func (_c *UserCreate) SetNillableUsername(v *string) *UserCreate {
return _c
}
-// SetWechat sets the "wechat" field.
-func (_c *UserCreate) SetWechat(v string) *UserCreate {
- _c.mutation.SetWechat(v)
- return _c
-}
-
-// SetNillableWechat sets the "wechat" field if the given value is not nil.
-func (_c *UserCreate) SetNillableWechat(v *string) *UserCreate {
- if v != nil {
- _c.SetWechat(*v)
- }
- return _c
-}
-
// SetNotes sets the "notes" field.
func (_c *UserCreate) SetNotes(v string) *UserCreate {
_c.mutation.SetNotes(v)
@@ -269,6 +256,21 @@ func (_c *UserCreate) AddUsageLogs(v ...*UsageLog) *UserCreate {
return _c.AddUsageLogIDs(ids...)
}
+// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
+func (_c *UserCreate) AddAttributeValueIDs(ids ...int64) *UserCreate {
+ _c.mutation.AddAttributeValueIDs(ids...)
+ return _c
+}
+
+// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
+func (_c *UserCreate) AddAttributeValues(v ...*UserAttributeValue) *UserCreate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _c.AddAttributeValueIDs(ids...)
+}
+
// Mutation returns the UserMutation object of the builder.
func (_c *UserCreate) Mutation() *UserMutation {
return _c.mutation
@@ -340,10 +342,6 @@ func (_c *UserCreate) defaults() error {
v := user.DefaultUsername
_c.mutation.SetUsername(v)
}
- if _, ok := _c.mutation.Wechat(); !ok {
- v := user.DefaultWechat
- _c.mutation.SetWechat(v)
- }
if _, ok := _c.mutation.Notes(); !ok {
v := user.DefaultNotes
_c.mutation.SetNotes(v)
@@ -405,14 +403,6 @@ func (_c *UserCreate) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
}
}
- if _, ok := _c.mutation.Wechat(); !ok {
- return &ValidationError{Name: "wechat", err: errors.New(`ent: missing required field "User.wechat"`)}
- }
- if v, ok := _c.mutation.Wechat(); ok {
- if err := user.WechatValidator(v); err != nil {
- return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
- }
- }
if _, ok := _c.mutation.Notes(); !ok {
return &ValidationError{Name: "notes", err: errors.New(`ent: missing required field "User.notes"`)}
}
@@ -483,10 +473,6 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
_spec.SetField(user.FieldUsername, field.TypeString, value)
_node.Username = value
}
- if value, ok := _c.mutation.Wechat(); ok {
- _spec.SetField(user.FieldWechat, field.TypeString, value)
- _node.Wechat = value
- }
if value, ok := _c.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value)
_node.Notes = value
@@ -591,6 +577,22 @@ func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
}
_spec.Edges = append(_spec.Edges, edge)
}
+ if nodes := _c.mutation.AttributeValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges = append(_spec.Edges, edge)
+ }
return _node, _spec
}
@@ -769,18 +771,6 @@ func (u *UserUpsert) UpdateUsername() *UserUpsert {
return u
}
-// SetWechat sets the "wechat" field.
-func (u *UserUpsert) SetWechat(v string) *UserUpsert {
- u.Set(user.FieldWechat, v)
- return u
-}
-
-// UpdateWechat sets the "wechat" field to the value that was provided on create.
-func (u *UserUpsert) UpdateWechat() *UserUpsert {
- u.SetExcluded(user.FieldWechat)
- return u
-}
-
// SetNotes sets the "notes" field.
func (u *UserUpsert) SetNotes(v string) *UserUpsert {
u.Set(user.FieldNotes, v)
@@ -985,20 +975,6 @@ func (u *UserUpsertOne) UpdateUsername() *UserUpsertOne {
})
}
-// SetWechat sets the "wechat" field.
-func (u *UserUpsertOne) SetWechat(v string) *UserUpsertOne {
- return u.Update(func(s *UserUpsert) {
- s.SetWechat(v)
- })
-}
-
-// UpdateWechat sets the "wechat" field to the value that was provided on create.
-func (u *UserUpsertOne) UpdateWechat() *UserUpsertOne {
- return u.Update(func(s *UserUpsert) {
- s.UpdateWechat()
- })
-}
-
// SetNotes sets the "notes" field.
func (u *UserUpsertOne) SetNotes(v string) *UserUpsertOne {
return u.Update(func(s *UserUpsert) {
@@ -1371,20 +1347,6 @@ func (u *UserUpsertBulk) UpdateUsername() *UserUpsertBulk {
})
}
-// SetWechat sets the "wechat" field.
-func (u *UserUpsertBulk) SetWechat(v string) *UserUpsertBulk {
- return u.Update(func(s *UserUpsert) {
- s.SetWechat(v)
- })
-}
-
-// UpdateWechat sets the "wechat" field to the value that was provided on create.
-func (u *UserUpsertBulk) UpdateWechat() *UserUpsertBulk {
- return u.Update(func(s *UserUpsert) {
- s.UpdateWechat()
- })
-}
-
// SetNotes sets the "notes" field.
func (u *UserUpsertBulk) SetNotes(v string) *UserUpsertBulk {
return u.Update(func(s *UserUpsert) {
diff --git a/backend/ent/user_query.go b/backend/ent/user_query.go
index c172dda3..80b182c1 100644
--- a/backend/ent/user_query.go
+++ b/backend/ent/user_query.go
@@ -19,6 +19,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -35,6 +36,7 @@ type UserQuery struct {
withAssignedSubscriptions *UserSubscriptionQuery
withAllowedGroups *GroupQuery
withUsageLogs *UsageLogQuery
+ withAttributeValues *UserAttributeValueQuery
withUserAllowedGroups *UserAllowedGroupQuery
// intermediate query (i.e. traversal path).
sql *sql.Selector
@@ -204,6 +206,28 @@ func (_q *UserQuery) QueryUsageLogs() *UsageLogQuery {
return query
}
+// QueryAttributeValues chains the current query on the "attribute_values" edge.
+func (_q *UserQuery) QueryAttributeValues() *UserAttributeValueQuery {
+ query := (&UserAttributeValueClient{config: _q.config}).Query()
+ query.path = func(ctx context.Context) (fromU *sql.Selector, err error) {
+ if err := _q.prepareQuery(ctx); err != nil {
+ return nil, err
+ }
+ selector := _q.sqlQuery(ctx)
+ if err := selector.Err(); err != nil {
+ return nil, err
+ }
+ step := sqlgraph.NewStep(
+ sqlgraph.From(user.Table, user.FieldID, selector),
+ sqlgraph.To(userattributevalue.Table, userattributevalue.FieldID),
+ sqlgraph.Edge(sqlgraph.O2M, false, user.AttributeValuesTable, user.AttributeValuesColumn),
+ )
+ fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step)
+ return fromU, nil
+ }
+ return query
+}
+
// QueryUserAllowedGroups chains the current query on the "user_allowed_groups" edge.
func (_q *UserQuery) QueryUserAllowedGroups() *UserAllowedGroupQuery {
query := (&UserAllowedGroupClient{config: _q.config}).Query()
@@ -424,6 +448,7 @@ func (_q *UserQuery) Clone() *UserQuery {
withAssignedSubscriptions: _q.withAssignedSubscriptions.Clone(),
withAllowedGroups: _q.withAllowedGroups.Clone(),
withUsageLogs: _q.withUsageLogs.Clone(),
+ withAttributeValues: _q.withAttributeValues.Clone(),
withUserAllowedGroups: _q.withUserAllowedGroups.Clone(),
// clone intermediate query.
sql: _q.sql.Clone(),
@@ -497,6 +522,17 @@ func (_q *UserQuery) WithUsageLogs(opts ...func(*UsageLogQuery)) *UserQuery {
return _q
}
+// WithAttributeValues tells the query-builder to eager-load the nodes that are connected to
+// the "attribute_values" edge. The optional arguments are used to configure the query builder of the edge.
+func (_q *UserQuery) WithAttributeValues(opts ...func(*UserAttributeValueQuery)) *UserQuery {
+ query := (&UserAttributeValueClient{config: _q.config}).Query()
+ for _, opt := range opts {
+ opt(query)
+ }
+ _q.withAttributeValues = query
+ return _q
+}
+
// WithUserAllowedGroups tells the query-builder to eager-load the nodes that are connected to
// the "user_allowed_groups" edge. The optional arguments are used to configure the query builder of the edge.
func (_q *UserQuery) WithUserAllowedGroups(opts ...func(*UserAllowedGroupQuery)) *UserQuery {
@@ -586,13 +622,14 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
var (
nodes = []*User{}
_spec = _q.querySpec()
- loadedTypes = [7]bool{
+ loadedTypes = [8]bool{
_q.withAPIKeys != nil,
_q.withRedeemCodes != nil,
_q.withSubscriptions != nil,
_q.withAssignedSubscriptions != nil,
_q.withAllowedGroups != nil,
_q.withUsageLogs != nil,
+ _q.withAttributeValues != nil,
_q.withUserAllowedGroups != nil,
}
)
@@ -658,6 +695,13 @@ func (_q *UserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*User, e
return nil, err
}
}
+ if query := _q.withAttributeValues; query != nil {
+ if err := _q.loadAttributeValues(ctx, query, nodes,
+ func(n *User) { n.Edges.AttributeValues = []*UserAttributeValue{} },
+ func(n *User, e *UserAttributeValue) { n.Edges.AttributeValues = append(n.Edges.AttributeValues, e) }); err != nil {
+ return nil, err
+ }
+ }
if query := _q.withUserAllowedGroups; query != nil {
if err := _q.loadUserAllowedGroups(ctx, query, nodes,
func(n *User) { n.Edges.UserAllowedGroups = []*UserAllowedGroup{} },
@@ -885,6 +929,36 @@ func (_q *UserQuery) loadUsageLogs(ctx context.Context, query *UsageLogQuery, no
}
return nil
}
+func (_q *UserQuery) loadAttributeValues(ctx context.Context, query *UserAttributeValueQuery, nodes []*User, init func(*User), assign func(*User, *UserAttributeValue)) error {
+ fks := make([]driver.Value, 0, len(nodes))
+ nodeids := make(map[int64]*User)
+ for i := range nodes {
+ fks = append(fks, nodes[i].ID)
+ nodeids[nodes[i].ID] = nodes[i]
+ if init != nil {
+ init(nodes[i])
+ }
+ }
+ if len(query.ctx.Fields) > 0 {
+ query.ctx.AppendFieldOnce(userattributevalue.FieldUserID)
+ }
+ query.Where(predicate.UserAttributeValue(func(s *sql.Selector) {
+ s.Where(sql.InValues(s.C(user.AttributeValuesColumn), fks...))
+ }))
+ neighbors, err := query.All(ctx)
+ if err != nil {
+ return err
+ }
+ for _, n := range neighbors {
+ fk := n.UserID
+ node, ok := nodeids[fk]
+ if !ok {
+ return fmt.Errorf(`unexpected referenced foreign-key "user_id" returned %v for node %v`, fk, n.ID)
+ }
+ assign(node, n)
+ }
+ return nil
+}
func (_q *UserQuery) loadUserAllowedGroups(ctx context.Context, query *UserAllowedGroupQuery, nodes []*User, init func(*User), assign func(*User, *UserAllowedGroup)) error {
fks := make([]driver.Value, 0, len(nodes))
nodeids := make(map[int64]*User)
diff --git a/backend/ent/user_update.go b/backend/ent/user_update.go
index 31e57a43..ed5d3a76 100644
--- a/backend/ent/user_update.go
+++ b/backend/ent/user_update.go
@@ -17,6 +17,7 @@ import (
"github.com/Wei-Shaw/sub2api/ent/redeemcode"
"github.com/Wei-Shaw/sub2api/ent/usagelog"
"github.com/Wei-Shaw/sub2api/ent/user"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
)
@@ -171,20 +172,6 @@ func (_u *UserUpdate) SetNillableUsername(v *string) *UserUpdate {
return _u
}
-// SetWechat sets the "wechat" field.
-func (_u *UserUpdate) SetWechat(v string) *UserUpdate {
- _u.mutation.SetWechat(v)
- return _u
-}
-
-// SetNillableWechat sets the "wechat" field if the given value is not nil.
-func (_u *UserUpdate) SetNillableWechat(v *string) *UserUpdate {
- if v != nil {
- _u.SetWechat(*v)
- }
- return _u
-}
-
// SetNotes sets the "notes" field.
func (_u *UserUpdate) SetNotes(v string) *UserUpdate {
_u.mutation.SetNotes(v)
@@ -289,6 +276,21 @@ func (_u *UserUpdate) AddUsageLogs(v ...*UsageLog) *UserUpdate {
return _u.AddUsageLogIDs(ids...)
}
+// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
+func (_u *UserUpdate) AddAttributeValueIDs(ids ...int64) *UserUpdate {
+ _u.mutation.AddAttributeValueIDs(ids...)
+ return _u
+}
+
+// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
+func (_u *UserUpdate) AddAttributeValues(v ...*UserAttributeValue) *UserUpdate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.AddAttributeValueIDs(ids...)
+}
+
// Mutation returns the UserMutation object of the builder.
func (_u *UserUpdate) Mutation() *UserMutation {
return _u.mutation
@@ -420,6 +422,27 @@ func (_u *UserUpdate) RemoveUsageLogs(v ...*UsageLog) *UserUpdate {
return _u.RemoveUsageLogIDs(ids...)
}
+// ClearAttributeValues clears all "attribute_values" edges to the UserAttributeValue entity.
+func (_u *UserUpdate) ClearAttributeValues() *UserUpdate {
+ _u.mutation.ClearAttributeValues()
+ return _u
+}
+
+// RemoveAttributeValueIDs removes the "attribute_values" edge to UserAttributeValue entities by IDs.
+func (_u *UserUpdate) RemoveAttributeValueIDs(ids ...int64) *UserUpdate {
+ _u.mutation.RemoveAttributeValueIDs(ids...)
+ return _u
+}
+
+// RemoveAttributeValues removes "attribute_values" edges to UserAttributeValue entities.
+func (_u *UserUpdate) RemoveAttributeValues(v ...*UserAttributeValue) *UserUpdate {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.RemoveAttributeValueIDs(ids...)
+}
+
// Save executes the query and returns the number of nodes affected by the update operation.
func (_u *UserUpdate) Save(ctx context.Context) (int, error) {
if err := _u.defaults(); err != nil {
@@ -489,11 +512,6 @@ func (_u *UserUpdate) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
}
}
- if v, ok := _u.mutation.Wechat(); ok {
- if err := user.WechatValidator(v); err != nil {
- return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
- }
- }
return nil
}
@@ -545,9 +563,6 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if value, ok := _u.mutation.Username(); ok {
_spec.SetField(user.FieldUsername, field.TypeString, value)
}
- if value, ok := _u.mutation.Wechat(); ok {
- _spec.SetField(user.FieldWechat, field.TypeString, value)
- }
if value, ok := _u.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value)
}
@@ -833,6 +848,51 @@ func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
+ if _u.mutation.AttributeValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.RemovedAttributeValuesIDs(); len(nodes) > 0 && !_u.mutation.AttributeValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.AttributeValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{user.Label}
@@ -991,20 +1051,6 @@ func (_u *UserUpdateOne) SetNillableUsername(v *string) *UserUpdateOne {
return _u
}
-// SetWechat sets the "wechat" field.
-func (_u *UserUpdateOne) SetWechat(v string) *UserUpdateOne {
- _u.mutation.SetWechat(v)
- return _u
-}
-
-// SetNillableWechat sets the "wechat" field if the given value is not nil.
-func (_u *UserUpdateOne) SetNillableWechat(v *string) *UserUpdateOne {
- if v != nil {
- _u.SetWechat(*v)
- }
- return _u
-}
-
// SetNotes sets the "notes" field.
func (_u *UserUpdateOne) SetNotes(v string) *UserUpdateOne {
_u.mutation.SetNotes(v)
@@ -1109,6 +1155,21 @@ func (_u *UserUpdateOne) AddUsageLogs(v ...*UsageLog) *UserUpdateOne {
return _u.AddUsageLogIDs(ids...)
}
+// AddAttributeValueIDs adds the "attribute_values" edge to the UserAttributeValue entity by IDs.
+func (_u *UserUpdateOne) AddAttributeValueIDs(ids ...int64) *UserUpdateOne {
+ _u.mutation.AddAttributeValueIDs(ids...)
+ return _u
+}
+
+// AddAttributeValues adds the "attribute_values" edges to the UserAttributeValue entity.
+func (_u *UserUpdateOne) AddAttributeValues(v ...*UserAttributeValue) *UserUpdateOne {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.AddAttributeValueIDs(ids...)
+}
+
// Mutation returns the UserMutation object of the builder.
func (_u *UserUpdateOne) Mutation() *UserMutation {
return _u.mutation
@@ -1240,6 +1301,27 @@ func (_u *UserUpdateOne) RemoveUsageLogs(v ...*UsageLog) *UserUpdateOne {
return _u.RemoveUsageLogIDs(ids...)
}
+// ClearAttributeValues clears all "attribute_values" edges to the UserAttributeValue entity.
+func (_u *UserUpdateOne) ClearAttributeValues() *UserUpdateOne {
+ _u.mutation.ClearAttributeValues()
+ return _u
+}
+
+// RemoveAttributeValueIDs removes the "attribute_values" edge to UserAttributeValue entities by IDs.
+func (_u *UserUpdateOne) RemoveAttributeValueIDs(ids ...int64) *UserUpdateOne {
+ _u.mutation.RemoveAttributeValueIDs(ids...)
+ return _u
+}
+
+// RemoveAttributeValues removes "attribute_values" edges to UserAttributeValue entities.
+func (_u *UserUpdateOne) RemoveAttributeValues(v ...*UserAttributeValue) *UserUpdateOne {
+ ids := make([]int64, len(v))
+ for i := range v {
+ ids[i] = v[i].ID
+ }
+ return _u.RemoveAttributeValueIDs(ids...)
+}
+
// Where appends a list predicates to the UserUpdate builder.
func (_u *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
_u.mutation.Where(ps...)
@@ -1322,11 +1404,6 @@ func (_u *UserUpdateOne) check() error {
return &ValidationError{Name: "username", err: fmt.Errorf(`ent: validator failed for field "User.username": %w`, err)}
}
}
- if v, ok := _u.mutation.Wechat(); ok {
- if err := user.WechatValidator(v); err != nil {
- return &ValidationError{Name: "wechat", err: fmt.Errorf(`ent: validator failed for field "User.wechat": %w`, err)}
- }
- }
return nil
}
@@ -1395,9 +1472,6 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
if value, ok := _u.mutation.Username(); ok {
_spec.SetField(user.FieldUsername, field.TypeString, value)
}
- if value, ok := _u.mutation.Wechat(); ok {
- _spec.SetField(user.FieldWechat, field.TypeString, value)
- }
if value, ok := _u.mutation.Notes(); ok {
_spec.SetField(user.FieldNotes, field.TypeString, value)
}
@@ -1683,6 +1757,51 @@ func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
}
_spec.Edges.Add = append(_spec.Edges.Add, edge)
}
+ if _u.mutation.AttributeValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.RemovedAttributeValuesIDs(); len(nodes) > 0 && !_u.mutation.AttributeValuesCleared() {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
+ }
+ if nodes := _u.mutation.AttributeValuesIDs(); len(nodes) > 0 {
+ edge := &sqlgraph.EdgeSpec{
+ Rel: sqlgraph.O2M,
+ Inverse: false,
+ Table: user.AttributeValuesTable,
+ Columns: []string{user.AttributeValuesColumn},
+ Bidi: false,
+ Target: &sqlgraph.EdgeTarget{
+ IDSpec: sqlgraph.NewFieldSpec(userattributevalue.FieldID, field.TypeInt64),
+ },
+ }
+ for _, k := range nodes {
+ edge.Target.Nodes = append(edge.Target.Nodes, k)
+ }
+ _spec.Edges.Add = append(_spec.Edges.Add, edge)
+ }
_node = &User{config: _u.config}
_spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues
diff --git a/backend/internal/handler/admin/usage_handler.go b/backend/internal/handler/admin/usage_handler.go
index 9a8e3244..a75948f7 100644
--- a/backend/internal/handler/admin/usage_handler.go
+++ b/backend/internal/handler/admin/usage_handler.go
@@ -246,7 +246,7 @@ func (h *UsageHandler) SearchUsers(c *gin.Context) {
}
// Limit to 30 results
- users, _, err := h.adminService.ListUsers(c.Request.Context(), 1, 30, "", "", keyword)
+ users, _, err := h.adminService.ListUsers(c.Request.Context(), 1, 30, service.UserListFilters{Search: keyword})
if err != nil {
response.ErrorFrom(c, err)
return
diff --git a/backend/internal/handler/admin/user_handler.go b/backend/internal/handler/admin/user_handler.go
index 681b59ea..11bdebd2 100644
--- a/backend/internal/handler/admin/user_handler.go
+++ b/backend/internal/handler/admin/user_handler.go
@@ -27,7 +27,6 @@ type CreateUserRequest struct {
Email string `json:"email" binding:"required,email"`
Password string `json:"password" binding:"required,min=6"`
Username string `json:"username"`
- Wechat string `json:"wechat"`
Notes string `json:"notes"`
Balance float64 `json:"balance"`
Concurrency int `json:"concurrency"`
@@ -40,7 +39,6 @@ type UpdateUserRequest struct {
Email string `json:"email" binding:"omitempty,email"`
Password string `json:"password" binding:"omitempty,min=6"`
Username *string `json:"username"`
- Wechat *string `json:"wechat"`
Notes *string `json:"notes"`
Balance *float64 `json:"balance"`
Concurrency *int `json:"concurrency"`
@@ -57,13 +55,22 @@ type UpdateBalanceRequest struct {
// List handles listing all users with pagination
// GET /api/v1/admin/users
+// Query params:
+// - status: filter by user status
+// - role: filter by user role
+// - search: search in email, username
+// - attr[{id}]: filter by custom attribute value, e.g. attr[1]=company
func (h *UserHandler) List(c *gin.Context) {
page, pageSize := response.ParsePagination(c)
- status := c.Query("status")
- role := c.Query("role")
- search := c.Query("search")
- users, total, err := h.adminService.ListUsers(c.Request.Context(), page, pageSize, status, role, search)
+ filters := service.UserListFilters{
+ Status: c.Query("status"),
+ Role: c.Query("role"),
+ Search: c.Query("search"),
+ Attributes: parseAttributeFilters(c),
+ }
+
+ users, total, err := h.adminService.ListUsers(c.Request.Context(), page, pageSize, filters)
if err != nil {
response.ErrorFrom(c, err)
return
@@ -76,6 +83,29 @@ func (h *UserHandler) List(c *gin.Context) {
response.Paginated(c, out, total, page, pageSize)
}
+// parseAttributeFilters extracts attribute filters from query params
+// Format: attr[{attributeID}]=value, e.g. attr[1]=company&attr[2]=developer
+func parseAttributeFilters(c *gin.Context) map[int64]string {
+ result := make(map[int64]string)
+
+ // Get all query params and look for attr[*] pattern
+ for key, values := range c.Request.URL.Query() {
+ if len(values) == 0 || values[0] == "" {
+ continue
+ }
+ // Check if key matches pattern attr[{id}]
+ if len(key) > 5 && key[:5] == "attr[" && key[len(key)-1] == ']' {
+ idStr := key[5 : len(key)-1]
+ id, err := strconv.ParseInt(idStr, 10, 64)
+ if err == nil && id > 0 {
+ result[id] = values[0]
+ }
+ }
+ }
+
+ return result
+}
+
// GetByID handles getting a user by ID
// GET /api/v1/admin/users/:id
func (h *UserHandler) GetByID(c *gin.Context) {
@@ -107,7 +137,6 @@ func (h *UserHandler) Create(c *gin.Context) {
Email: req.Email,
Password: req.Password,
Username: req.Username,
- Wechat: req.Wechat,
Notes: req.Notes,
Balance: req.Balance,
Concurrency: req.Concurrency,
@@ -141,7 +170,6 @@ func (h *UserHandler) Update(c *gin.Context) {
Email: req.Email,
Password: req.Password,
Username: req.Username,
- Wechat: req.Wechat,
Notes: req.Notes,
Balance: req.Balance,
Concurrency: req.Concurrency,
diff --git a/backend/internal/handler/dto/mappers.go b/backend/internal/handler/dto/mappers.go
index a3d71c6b..f94bb7c2 100644
--- a/backend/internal/handler/dto/mappers.go
+++ b/backend/internal/handler/dto/mappers.go
@@ -10,7 +10,6 @@ func UserFromServiceShallow(u *service.User) *User {
ID: u.ID,
Email: u.Email,
Username: u.Username,
- Wechat: u.Wechat,
Notes: u.Notes,
Role: u.Role,
Balance: u.Balance,
diff --git a/backend/internal/handler/dto/types.go b/backend/internal/handler/dto/types.go
index 24d78e98..75021875 100644
--- a/backend/internal/handler/dto/types.go
+++ b/backend/internal/handler/dto/types.go
@@ -6,7 +6,6 @@ type User struct {
ID int64 `json:"id"`
Email string `json:"email"`
Username string `json:"username"`
- Wechat string `json:"wechat"`
Notes string `json:"notes"`
Role string `json:"role"`
Balance float64 `json:"balance"`
diff --git a/backend/internal/handler/user_handler.go b/backend/internal/handler/user_handler.go
index f4639b1f..d968951c 100644
--- a/backend/internal/handler/user_handler.go
+++ b/backend/internal/handler/user_handler.go
@@ -30,7 +30,6 @@ type ChangePasswordRequest struct {
// UpdateProfileRequest represents the update profile request payload
type UpdateProfileRequest struct {
Username *string `json:"username"`
- Wechat *string `json:"wechat"`
}
// GetProfile handles getting user profile
@@ -99,7 +98,6 @@ func (h *UserHandler) UpdateProfile(c *gin.Context) {
svcReq := service.UpdateProfileRequest{
Username: req.Username,
- Wechat: req.Wechat,
}
updatedUser, err := h.userService.UpdateProfile(c.Request.Context(), subject.UserID, svcReq)
if err != nil {
diff --git a/backend/internal/repository/api_key_repo.go b/backend/internal/repository/api_key_repo.go
index 3ba2fd85..9fcee1ca 100644
--- a/backend/internal/repository/api_key_repo.go
+++ b/backend/internal/repository/api_key_repo.go
@@ -294,7 +294,6 @@ func userEntityToService(u *dbent.User) *service.User {
ID: u.ID,
Email: u.Email,
Username: u.Username,
- Wechat: u.Wechat,
Notes: u.Notes,
PasswordHash: u.PasswordHash,
Role: u.Role,
diff --git a/backend/internal/repository/fixtures_integration_test.go b/backend/internal/repository/fixtures_integration_test.go
index 8f13c532..ab8e8a4f 100644
--- a/backend/internal/repository/fixtures_integration_test.go
+++ b/backend/internal/repository/fixtures_integration_test.go
@@ -40,7 +40,6 @@ func mustCreateUser(t *testing.T, client *dbent.Client, u *service.User) *servic
SetBalance(u.Balance).
SetConcurrency(u.Concurrency).
SetUsername(u.Username).
- SetWechat(u.Wechat).
SetNotes(u.Notes)
if !u.CreatedAt.IsZero() {
create.SetCreatedAt(u.CreatedAt)
diff --git a/backend/internal/repository/migrations_schema_integration_test.go b/backend/internal/repository/migrations_schema_integration_test.go
index 4c7848b2..e8f652c4 100644
--- a/backend/internal/repository/migrations_schema_integration_test.go
+++ b/backend/internal/repository/migrations_schema_integration_test.go
@@ -23,7 +23,6 @@ func TestMigrationsRunner_IsIdempotent_AndSchemaIsUpToDate(t *testing.T) {
// users: columns required by repository queries
requireColumn(t, tx, "users", "username", "character varying", 100, false)
- requireColumn(t, tx, "users", "wechat", "character varying", 100, false)
requireColumn(t, tx, "users", "notes", "text", 0, false)
// accounts: schedulable and rate-limit fields
diff --git a/backend/internal/repository/user_repo.go b/backend/internal/repository/user_repo.go
index 8393ae7c..57c2ef83 100644
--- a/backend/internal/repository/user_repo.go
+++ b/backend/internal/repository/user_repo.go
@@ -9,6 +9,7 @@ import (
dbent "github.com/Wei-Shaw/sub2api/ent"
dbuser "github.com/Wei-Shaw/sub2api/ent/user"
"github.com/Wei-Shaw/sub2api/ent/userallowedgroup"
+ "github.com/Wei-Shaw/sub2api/ent/userattributevalue"
"github.com/Wei-Shaw/sub2api/ent/usersubscription"
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
"github.com/Wei-Shaw/sub2api/internal/service"
@@ -50,7 +51,6 @@ func (r *userRepository) Create(ctx context.Context, userIn *service.User) error
created, err := txClient.User.Create().
SetEmail(userIn.Email).
SetUsername(userIn.Username).
- SetWechat(userIn.Wechat).
SetNotes(userIn.Notes).
SetPasswordHash(userIn.PasswordHash).
SetRole(userIn.Role).
@@ -133,7 +133,6 @@ func (r *userRepository) Update(ctx context.Context, userIn *service.User) error
updated, err := txClient.User.UpdateOneID(userIn.ID).
SetEmail(userIn.Email).
SetUsername(userIn.Username).
- SetWechat(userIn.Wechat).
SetNotes(userIn.Notes).
SetPasswordHash(userIn.PasswordHash).
SetRole(userIn.Role).
@@ -171,28 +170,38 @@ func (r *userRepository) Delete(ctx context.Context, id int64) error {
}
func (r *userRepository) List(ctx context.Context, params pagination.PaginationParams) ([]service.User, *pagination.PaginationResult, error) {
- return r.ListWithFilters(ctx, params, "", "", "")
+ return r.ListWithFilters(ctx, params, service.UserListFilters{})
}
-func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]service.User, *pagination.PaginationResult, error) {
+func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters service.UserListFilters) ([]service.User, *pagination.PaginationResult, error) {
q := r.client.User.Query()
- if status != "" {
- q = q.Where(dbuser.StatusEQ(status))
+ if filters.Status != "" {
+ q = q.Where(dbuser.StatusEQ(filters.Status))
}
- if role != "" {
- q = q.Where(dbuser.RoleEQ(role))
+ if filters.Role != "" {
+ q = q.Where(dbuser.RoleEQ(filters.Role))
}
- if search != "" {
+ if filters.Search != "" {
q = q.Where(
dbuser.Or(
- dbuser.EmailContainsFold(search),
- dbuser.UsernameContainsFold(search),
- dbuser.WechatContainsFold(search),
+ dbuser.EmailContainsFold(filters.Search),
+ dbuser.UsernameContainsFold(filters.Search),
),
)
}
+ // If attribute filters are specified, we need to filter by user IDs first
+ var allowedUserIDs []int64
+ if len(filters.Attributes) > 0 {
+ allowedUserIDs = r.filterUsersByAttributes(ctx, filters.Attributes)
+ if len(allowedUserIDs) == 0 {
+ // No users match the attribute filters
+ return []service.User{}, paginationResultFromTotal(0, params), nil
+ }
+ q = q.Where(dbuser.IDIn(allowedUserIDs...))
+ }
+
total, err := q.Clone().Count(ctx)
if err != nil {
return nil, nil, err
@@ -252,6 +261,59 @@ func (r *userRepository) ListWithFilters(ctx context.Context, params pagination.
return outUsers, paginationResultFromTotal(int64(total), params), nil
}
+// filterUsersByAttributes returns user IDs that match ALL the given attribute filters
+func (r *userRepository) filterUsersByAttributes(ctx context.Context, attrs map[int64]string) []int64 {
+ if len(attrs) == 0 {
+ return nil
+ }
+
+ // For each attribute filter, get the set of matching user IDs
+ // Then intersect all sets to get users matching ALL filters
+ var resultSet map[int64]struct{}
+ first := true
+
+ for attrID, value := range attrs {
+ // Query user_attribute_values for this attribute
+ values, err := r.client.UserAttributeValue.Query().
+ Where(
+ userattributevalue.AttributeIDEQ(attrID),
+ userattributevalue.ValueContainsFold(value),
+ ).
+ All(ctx)
+ if err != nil {
+ continue
+ }
+
+ currentSet := make(map[int64]struct{}, len(values))
+ for _, v := range values {
+ currentSet[v.UserID] = struct{}{}
+ }
+
+ if first {
+ resultSet = currentSet
+ first = false
+ } else {
+ // Intersect with previous results
+ for userID := range resultSet {
+ if _, ok := currentSet[userID]; !ok {
+ delete(resultSet, userID)
+ }
+ }
+ }
+
+ // Early exit if no users match
+ if len(resultSet) == 0 {
+ return nil
+ }
+ }
+
+ result := make([]int64, 0, len(resultSet))
+ for userID := range resultSet {
+ result = append(result, userID)
+ }
+ return result
+}
+
func (r *userRepository) UpdateBalance(ctx context.Context, id int64, amount float64) error {
client := clientFromContext(ctx, r.client)
n, err := client.User.Update().Where(dbuser.IDEQ(id)).AddBalance(amount).Save(ctx)
diff --git a/backend/internal/repository/user_repo_integration_test.go b/backend/internal/repository/user_repo_integration_test.go
index 55db00c3..c492fda6 100644
--- a/backend/internal/repository/user_repo_integration_test.go
+++ b/backend/internal/repository/user_repo_integration_test.go
@@ -202,16 +202,6 @@ func (s *UserRepoSuite) TestListWithFilters_SearchByUsername() {
s.Require().Equal("JohnDoe", users[0].Username)
}
-func (s *UserRepoSuite) TestListWithFilters_SearchByWechat() {
- s.mustCreateUser(&service.User{Email: "w1@test.com", Wechat: "wx_hello"})
- s.mustCreateUser(&service.User{Email: "w2@test.com", Wechat: "wx_world"})
-
- users, _, err := s.repo.ListWithFilters(s.ctx, pagination.PaginationParams{Page: 1, PageSize: 10}, "", "", "wx_hello")
- s.Require().NoError(err)
- s.Require().Len(users, 1)
- s.Require().Equal("wx_hello", users[0].Wechat)
-}
-
func (s *UserRepoSuite) TestListWithFilters_LoadsActiveSubscriptions() {
user := s.mustCreateUser(&service.User{Email: "sub@test.com", Status: service.StatusActive})
groupActive := s.mustCreateGroup("g-sub-active")
@@ -238,7 +228,6 @@ func (s *UserRepoSuite) TestListWithFilters_CombinedFilters() {
s.mustCreateUser(&service.User{
Email: "a@example.com",
Username: "Alice",
- Wechat: "wx_a",
Role: service.RoleUser,
Status: service.StatusActive,
Balance: 10,
@@ -246,7 +235,6 @@ func (s *UserRepoSuite) TestListWithFilters_CombinedFilters() {
target := s.mustCreateUser(&service.User{
Email: "b@example.com",
Username: "Bob",
- Wechat: "wx_b",
Role: service.RoleAdmin,
Status: service.StatusActive,
Balance: 1,
@@ -448,7 +436,6 @@ func (s *UserRepoSuite) TestCRUD_And_Filters_And_AtomicUpdates() {
user1 := s.mustCreateUser(&service.User{
Email: "a@example.com",
Username: "Alice",
- Wechat: "wx_a",
Role: service.RoleUser,
Status: service.StatusActive,
Balance: 10,
@@ -456,7 +443,6 @@ func (s *UserRepoSuite) TestCRUD_And_Filters_And_AtomicUpdates() {
user2 := s.mustCreateUser(&service.User{
Email: "b@example.com",
Username: "Bob",
- Wechat: "wx_b",
Role: service.RoleAdmin,
Status: service.StatusActive,
Balance: 1,
diff --git a/backend/internal/server/api_contract_test.go b/backend/internal/server/api_contract_test.go
index 3912c8fb..51f5e43d 100644
--- a/backend/internal/server/api_contract_test.go
+++ b/backend/internal/server/api_contract_test.go
@@ -51,7 +51,6 @@ func TestAPIContracts(t *testing.T) {
"id": 1,
"email": "alice@example.com",
"username": "alice",
- "wechat": "wx_alice",
"notes": "hello",
"role": "user",
"balance": 12.5,
diff --git a/backend/internal/service/admin_service.go b/backend/internal/service/admin_service.go
index feeb19a0..e3ff0fbd 100644
--- a/backend/internal/service/admin_service.go
+++ b/backend/internal/service/admin_service.go
@@ -13,7 +13,7 @@ import (
// AdminService interface defines admin management operations
type AdminService interface {
// User management
- ListUsers(ctx context.Context, page, pageSize int, status, role, search string) ([]User, int64, error)
+ ListUsers(ctx context.Context, page, pageSize int, filters UserListFilters) ([]User, int64, error)
GetUser(ctx context.Context, id int64) (*User, error)
CreateUser(ctx context.Context, input *CreateUserInput) (*User, error)
UpdateUser(ctx context.Context, id int64, input *UpdateUserInput) (*User, error)
@@ -69,7 +69,6 @@ type CreateUserInput struct {
Email string
Password string
Username string
- Wechat string
Notes string
Balance float64
Concurrency int
@@ -80,7 +79,6 @@ type UpdateUserInput struct {
Email string
Password string
Username *string
- Wechat *string
Notes *string
Balance *float64 // 使用指针区分"未提供"和"设置为0"
Concurrency *int // 使用指针区分"未提供"和"设置为0"
@@ -251,9 +249,9 @@ func NewAdminService(
}
// User management implementations
-func (s *adminServiceImpl) ListUsers(ctx context.Context, page, pageSize int, status, role, search string) ([]User, int64, error) {
+func (s *adminServiceImpl) ListUsers(ctx context.Context, page, pageSize int, filters UserListFilters) ([]User, int64, error) {
params := pagination.PaginationParams{Page: page, PageSize: pageSize}
- users, result, err := s.userRepo.ListWithFilters(ctx, params, status, role, search)
+ users, result, err := s.userRepo.ListWithFilters(ctx, params, filters)
if err != nil {
return nil, 0, err
}
@@ -268,7 +266,6 @@ func (s *adminServiceImpl) CreateUser(ctx context.Context, input *CreateUserInpu
user := &User{
Email: input.Email,
Username: input.Username,
- Wechat: input.Wechat,
Notes: input.Notes,
Role: RoleUser, // Always create as regular user, never admin
Balance: input.Balance,
@@ -310,9 +307,6 @@ func (s *adminServiceImpl) UpdateUser(ctx context.Context, id int64, input *Upda
if input.Username != nil {
user.Username = *input.Username
}
- if input.Wechat != nil {
- user.Wechat = *input.Wechat
- }
if input.Notes != nil {
user.Notes = *input.Notes
}
diff --git a/backend/internal/service/admin_service_create_user_test.go b/backend/internal/service/admin_service_create_user_test.go
index cfa52de8..a0fe4d87 100644
--- a/backend/internal/service/admin_service_create_user_test.go
+++ b/backend/internal/service/admin_service_create_user_test.go
@@ -18,7 +18,6 @@ func TestAdminService_CreateUser_Success(t *testing.T) {
Email: "user@test.com",
Password: "strong-pass",
Username: "tester",
- Wechat: "wx",
Notes: "note",
Balance: 12.5,
Concurrency: 7,
@@ -31,7 +30,6 @@ func TestAdminService_CreateUser_Success(t *testing.T) {
require.Equal(t, int64(10), user.ID)
require.Equal(t, input.Email, user.Email)
require.Equal(t, input.Username, user.Username)
- require.Equal(t, input.Wechat, user.Wechat)
require.Equal(t, input.Notes, user.Notes)
require.Equal(t, input.Balance, user.Balance)
require.Equal(t, input.Concurrency, user.Concurrency)
diff --git a/backend/internal/service/user.go b/backend/internal/service/user.go
index fe670202..894243df 100644
--- a/backend/internal/service/user.go
+++ b/backend/internal/service/user.go
@@ -10,7 +10,6 @@ type User struct {
ID int64
Email string
Username string
- Wechat string
Notes string
PasswordHash string
Role string
diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go
index 44a94d32..d5e1f869 100644
--- a/backend/internal/service/user_service.go
+++ b/backend/internal/service/user_service.go
@@ -14,6 +14,14 @@ var (
ErrInsufficientPerms = infraerrors.Forbidden("INSUFFICIENT_PERMISSIONS", "insufficient permissions")
)
+// UserListFilters contains all filter options for listing users
+type UserListFilters struct {
+ Status string // User status filter
+ Role string // User role filter
+ Search string // Search in email, username
+ Attributes map[int64]string // Custom attribute filters: attributeID -> value
+}
+
type UserRepository interface {
Create(ctx context.Context, user *User) error
GetByID(ctx context.Context, id int64) (*User, error)
@@ -23,7 +31,7 @@ type UserRepository interface {
Delete(ctx context.Context, id int64) error
List(ctx context.Context, params pagination.PaginationParams) ([]User, *pagination.PaginationResult, error)
- ListWithFilters(ctx context.Context, params pagination.PaginationParams, status, role, search string) ([]User, *pagination.PaginationResult, error)
+ ListWithFilters(ctx context.Context, params pagination.PaginationParams, filters UserListFilters) ([]User, *pagination.PaginationResult, error)
UpdateBalance(ctx context.Context, id int64, amount float64) error
DeductBalance(ctx context.Context, id int64, amount float64) error
@@ -36,7 +44,6 @@ type UserRepository interface {
type UpdateProfileRequest struct {
Email *string `json:"email"`
Username *string `json:"username"`
- Wechat *string `json:"wechat"`
Concurrency *int `json:"concurrency"`
}
@@ -100,10 +107,6 @@ func (s *UserService) UpdateProfile(ctx context.Context, userID int64, req Updat
user.Username = *req.Username
}
- if req.Wechat != nil {
- user.Wechat = *req.Wechat
- }
-
if req.Concurrency != nil {
user.Concurrency = *req.Concurrency
}
diff --git a/backend/migrations/019_migrate_wechat_to_attributes.sql b/backend/migrations/019_migrate_wechat_to_attributes.sql
new file mode 100644
index 00000000..765ca498
--- /dev/null
+++ b/backend/migrations/019_migrate_wechat_to_attributes.sql
@@ -0,0 +1,83 @@
+-- Migration: Move wechat field from users table to user_attribute_values
+-- This migration:
+-- 1. Creates a "wechat" attribute definition
+-- 2. Migrates existing wechat data to user_attribute_values
+-- 3. Does NOT drop the wechat column (for rollback safety, can be done in a later migration)
+
+-- +goose Up
+-- +goose StatementBegin
+
+-- Step 1: Insert wechat attribute definition if not exists
+INSERT INTO user_attribute_definitions (key, name, description, type, options, required, validation, placeholder, display_order, enabled, created_at, updated_at)
+SELECT 'wechat', '微信', '用户微信号', 'text', '[]'::jsonb, false, '{}'::jsonb, '请输入微信号', 0, true, NOW(), NOW()
+WHERE NOT EXISTS (
+ SELECT 1 FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL
+);
+
+-- Step 2: Migrate existing wechat values to user_attribute_values
+-- Only migrate non-empty values
+INSERT INTO user_attribute_values (user_id, attribute_id, value, created_at, updated_at)
+SELECT
+ u.id,
+ (SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL LIMIT 1),
+ u.wechat,
+ NOW(),
+ NOW()
+FROM users u
+WHERE u.wechat IS NOT NULL
+ AND u.wechat != ''
+ AND u.deleted_at IS NULL
+ AND NOT EXISTS (
+ SELECT 1 FROM user_attribute_values uav
+ WHERE uav.user_id = u.id
+ AND uav.attribute_id = (SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL LIMIT 1)
+ );
+
+-- Step 3: Update display_order to ensure wechat appears first
+UPDATE user_attribute_definitions
+SET display_order = -1
+WHERE key = 'wechat' AND deleted_at IS NULL;
+
+-- Reorder all attributes starting from 0
+WITH ordered AS (
+ SELECT id, ROW_NUMBER() OVER (ORDER BY display_order, id) - 1 as new_order
+ FROM user_attribute_definitions
+ WHERE deleted_at IS NULL
+)
+UPDATE user_attribute_definitions
+SET display_order = ordered.new_order
+FROM ordered
+WHERE user_attribute_definitions.id = ordered.id;
+
+-- Step 4: Drop the redundant wechat column from users table
+ALTER TABLE users DROP COLUMN IF EXISTS wechat;
+
+-- +goose StatementEnd
+
+-- +goose Down
+-- +goose StatementBegin
+
+-- Restore wechat column
+ALTER TABLE users ADD COLUMN IF NOT EXISTS wechat VARCHAR(100) DEFAULT '';
+
+-- Copy attribute values back to users.wechat column
+UPDATE users u
+SET wechat = uav.value
+FROM user_attribute_values uav
+JOIN user_attribute_definitions uad ON uav.attribute_id = uad.id
+WHERE uav.user_id = u.id
+ AND uad.key = 'wechat'
+ AND uad.deleted_at IS NULL;
+
+-- Delete migrated attribute values
+DELETE FROM user_attribute_values
+WHERE attribute_id IN (
+ SELECT id FROM user_attribute_definitions WHERE key = 'wechat' AND deleted_at IS NULL
+);
+
+-- Soft-delete the wechat attribute definition
+UPDATE user_attribute_definitions
+SET deleted_at = NOW()
+WHERE key = 'wechat' AND deleted_at IS NULL;
+
+-- +goose StatementEnd
diff --git a/frontend/src/api/admin/users.ts b/frontend/src/api/admin/users.ts
index 2901f4ce..44963cf9 100644
--- a/frontend/src/api/admin/users.ts
+++ b/frontend/src/api/admin/users.ts
@@ -10,7 +10,7 @@ import type { User, UpdateUserRequest, PaginatedResponse } from '@/types'
* List all users with pagination
* @param page - Page number (default: 1)
* @param pageSize - Items per page (default: 20)
- * @param filters - Optional filters (status, role, search)
+ * @param filters - Optional filters (status, role, search, attributes)
* @param options - Optional request options (signal)
* @returns Paginated list of users
*/
@@ -21,17 +21,32 @@ export async function list(
status?: 'active' | 'disabled'
role?: 'admin' | 'user'
search?: string
+ attributes?: Record // attributeId -> value
},
options?: {
signal?: AbortSignal
}
): Promise> {
+ // Build params with attribute filters in attr[id]=value format
+ const params: Record = {
+ page,
+ page_size: pageSize,
+ status: filters?.status,
+ role: filters?.role,
+ search: filters?.search
+ }
+
+ // Add attribute filters as attr[id]=value
+ if (filters?.attributes) {
+ for (const [attrId, value] of Object.entries(filters.attributes)) {
+ if (value) {
+ params[`attr[${attrId}]`] = value
+ }
+ }
+ }
+
const { data } = await apiClient.get>('/admin/users', {
- params: {
- page,
- page_size: pageSize,
- ...filters
- },
+ params,
signal: options?.signal
})
return data
diff --git a/frontend/src/api/user.ts b/frontend/src/api/user.ts
index d34ce20e..bfc0e30b 100644
--- a/frontend/src/api/user.ts
+++ b/frontend/src/api/user.ts
@@ -22,7 +22,6 @@ export async function getProfile(): Promise {
*/
export async function updateProfile(profile: {
username?: string
- wechat?: string
}): Promise {
const { data } = await apiClient.put('/user', profile)
return data
diff --git a/frontend/src/i18n/locales/en.ts b/frontend/src/i18n/locales/en.ts
index fabf8775..f11afef6 100644
--- a/frontend/src/i18n/locales/en.ts
+++ b/frontend/src/i18n/locales/en.ts
@@ -434,9 +434,7 @@ export default {
administrator: 'Administrator',
user: 'User',
username: 'Username',
- wechat: 'WeChat ID',
enterUsername: 'Enter username',
- enterWechat: 'Enter WeChat ID',
editProfile: 'Edit Profile',
updateProfile: 'Update Profile',
updating: 'Updating...',
@@ -565,12 +563,10 @@ export default {
email: 'Email',
password: 'Password',
username: 'Username',
- wechat: 'WeChat ID',
notes: 'Notes',
enterEmail: 'Enter email',
enterPassword: 'Enter password',
enterUsername: 'Enter username (optional)',
- enterWechat: 'Enter WeChat ID (optional)',
enterNotes: 'Enter notes (admin only)',
notesHint: 'This note is only visible to administrators',
enterNewPassword: 'Enter new password (optional)',
@@ -582,7 +578,6 @@ export default {
columns: {
user: 'User',
username: 'Username',
- wechat: 'WeChat ID',
notes: 'Notes',
role: 'Role',
subscriptions: 'Subscriptions',
@@ -653,7 +648,67 @@ export default {
failedToDeposit: 'Failed to deposit',
failedToWithdraw: 'Failed to withdraw',
useDepositWithdrawButtons: 'Please use deposit/withdraw buttons to adjust balance',
- insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal'
+ insufficientBalance: 'Insufficient balance, balance cannot be negative after withdrawal',
+ // Settings Dropdowns
+ filterSettings: 'Filter Settings',
+ columnSettings: 'Column Settings',
+ filterValue: 'Enter value',
+ // User Attributes
+ attributes: {
+ title: 'User Attributes',
+ description: 'Configure custom user attribute fields',
+ configButton: 'Attributes',
+ addAttribute: 'Add Attribute',
+ editAttribute: 'Edit Attribute',
+ deleteAttribute: 'Delete Attribute',
+ deleteConfirm: "Are you sure you want to delete attribute '{name}'? All user values for this attribute will be deleted.",
+ noAttributes: 'No custom attributes',
+ noAttributesHint: 'Click the button above to add custom attributes',
+ key: 'Attribute Key',
+ keyHint: 'For programmatic reference, only letters, numbers and underscores',
+ name: 'Display Name',
+ nameHint: 'Name shown in forms',
+ type: 'Attribute Type',
+ fieldDescription: 'Description',
+ fieldDescriptionHint: 'Description text for the attribute',
+ placeholder: 'Placeholder',
+ placeholderHint: 'Placeholder text for input field',
+ required: 'Required',
+ enabled: 'Enabled',
+ options: 'Options',
+ optionsHint: 'For select/multi-select types',
+ addOption: 'Add Option',
+ optionValue: 'Option Value',
+ optionLabel: 'Display Text',
+ validation: 'Validation Rules',
+ minLength: 'Min Length',
+ maxLength: 'Max Length',
+ min: 'Min Value',
+ max: 'Max Value',
+ pattern: 'Regex Pattern',
+ patternMessage: 'Validation Error Message',
+ types: {
+ text: 'Text',
+ textarea: 'Textarea',
+ number: 'Number',
+ email: 'Email',
+ url: 'URL',
+ date: 'Date',
+ select: 'Select',
+ multi_select: 'Multi-Select'
+ },
+ created: 'Attribute created successfully',
+ updated: 'Attribute updated successfully',
+ deleted: 'Attribute deleted successfully',
+ reordered: 'Attribute order updated successfully',
+ failedToLoad: 'Failed to load attributes',
+ failedToCreate: 'Failed to create attribute',
+ failedToUpdate: 'Failed to update attribute',
+ failedToDelete: 'Failed to delete attribute',
+ failedToReorder: 'Failed to update order',
+ keyExists: 'Attribute key already exists',
+ dragToReorder: 'Drag to reorder'
+ }
},
// Groups
diff --git a/frontend/src/i18n/locales/zh.ts b/frontend/src/i18n/locales/zh.ts
index dc9a19e5..6805a3c7 100644
--- a/frontend/src/i18n/locales/zh.ts
+++ b/frontend/src/i18n/locales/zh.ts
@@ -430,9 +430,7 @@ export default {
administrator: '管理员',
user: '用户',
username: '用户名',
- wechat: '微信号',
enterUsername: '输入用户名',
- enterWechat: '输入微信号',
editProfile: '编辑个人资料',
updateProfile: '更新资料',
updating: '更新中...',
@@ -583,12 +581,10 @@ export default {
email: '邮箱',
password: '密码',
username: '用户名',
- wechat: '微信号',
notes: '备注',
enterEmail: '请输入邮箱',
enterPassword: '请输入密码',
enterUsername: '请输入用户名(选填)',
- enterWechat: '请输入微信号(选填)',
enterNotes: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见',
enterNewPassword: '请输入新密码(选填)',
@@ -601,7 +597,6 @@ export default {
user: '用户',
email: '邮箱',
username: '用户名',
- wechat: '微信号',
notes: '备注',
role: '角色',
subscriptions: '订阅分组',
@@ -655,8 +650,6 @@ export default {
emailPlaceholder: '请输入邮箱',
usernameLabel: '用户名',
usernamePlaceholder: '请输入用户名(选填)',
- wechatLabel: '微信号',
- wechatPlaceholder: '请输入微信号(选填)',
notesLabel: '备注',
notesPlaceholder: '请输入备注(仅管理员可见)',
notesHint: '此备注仅对管理员可见',
@@ -711,7 +704,67 @@ export default {
failedToDeposit: '充值失败',
failedToWithdraw: '退款失败',
useDepositWithdrawButtons: '请使用充值/退款按钮调整余额',
- insufficientBalance: '余额不足,退款后余额不能为负数'
+ insufficientBalance: '余额不足,退款后余额不能为负数',
+ // Settings Dropdowns
+ filterSettings: '筛选设置',
+ columnSettings: '列设置',
+ filterValue: '输入值',
+ // User Attributes
+ attributes: {
+ title: '用户属性配置',
+ description: '配置用户的自定义属性字段',
+ configButton: '属性配置',
+ addAttribute: '添加属性',
+ editAttribute: '编辑属性',
+ deleteAttribute: '删除属性',
+ deleteConfirm: "确定要删除属性 '{name}' 吗?所有用户的该属性值将被删除。",
+ noAttributes: '暂无自定义属性',
+ noAttributesHint: '点击上方按钮添加自定义属性',
+ key: '属性键',
+ keyHint: '用于程序引用,只能包含字母、数字和下划线',
+ name: '显示名称',
+ nameHint: '在表单中显示的名称',
+ type: '属性类型',
+ fieldDescription: '描述',
+ fieldDescriptionHint: '属性的说明文字',
+ placeholder: '占位符',
+ placeholderHint: '输入框的提示文字',
+ required: '必填',
+ enabled: '启用',
+ options: '选项配置',
+ optionsHint: '用于单选/多选类型',
+ addOption: '添加选项',
+ optionValue: '选项值',
+ optionLabel: '显示文本',
+ validation: '验证规则',
+ minLength: '最小长度',
+ maxLength: '最大长度',
+ min: '最小值',
+ max: '最大值',
+ pattern: '正则表达式',
+ patternMessage: '验证失败提示',
+ types: {
+ text: '单行文本',
+ textarea: '多行文本',
+ number: '数字',
+ email: '邮箱',
+ url: '链接',
+ date: '日期',
+ select: '单选',
+ multi_select: '多选'
+ },
+ created: '属性创建成功',
+ updated: '属性更新成功',
+ deleted: '属性删除成功',
+ reordered: '属性排序更新成功',
+ failedToLoad: '加载属性列表失败',
+ failedToCreate: '创建属性失败',
+ failedToUpdate: '更新属性失败',
+ failedToDelete: '删除属性失败',
+ failedToReorder: '更新排序失败',
+ keyExists: '属性键已存在',
+ dragToReorder: '拖拽排序'
+ }
},
// Groups Management
diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts
index 80488602..47155a5d 100644
--- a/frontend/src/types/index.ts
+++ b/frontend/src/types/index.ts
@@ -7,7 +7,6 @@
export interface User {
id: number
username: string
- wechat: string
notes: string
email: string
role: 'admin' | 'user' // User role for authorization
@@ -634,7 +633,6 @@ export interface UpdateUserRequest {
email?: string
password?: string
username?: string
- wechat?: string
notes?: string
role?: 'admin' | 'user'
balance?: number
@@ -771,3 +769,76 @@ export interface AccountUsageStatsResponse {
summary: AccountUsageSummary
models: ModelStat[]
}
+
+// ==================== User Attribute Types ====================
+
+export type UserAttributeType = 'text' | 'textarea' | 'number' | 'email' | 'url' | 'date' | 'select' | 'multi_select'
+
+export interface UserAttributeOption {
+ value: string
+ label: string
+}
+
+export interface UserAttributeValidation {
+ min_length?: number
+ max_length?: number
+ min?: number
+ max?: number
+ pattern?: string
+ message?: string
+}
+
+export interface UserAttributeDefinition {
+ id: number
+ key: string
+ name: string
+ description: string
+ type: UserAttributeType
+ options: UserAttributeOption[]
+ required: boolean
+ validation: UserAttributeValidation
+ placeholder: string
+ display_order: number
+ enabled: boolean
+ created_at: string
+ updated_at: string
+}
+
+export interface UserAttributeValue {
+ id: number
+ user_id: number
+ attribute_id: number
+ value: string
+ created_at: string
+ updated_at: string
+}
+
+export interface CreateUserAttributeRequest {
+ key: string
+ name: string
+ description?: string
+ type: UserAttributeType
+ options?: UserAttributeOption[]
+ required?: boolean
+ validation?: UserAttributeValidation
+ placeholder?: string
+ display_order?: number
+ enabled?: boolean
+}
+
+export interface UpdateUserAttributeRequest {
+ key?: string
+ name?: string
+ description?: string
+ type?: UserAttributeType
+ options?: UserAttributeOption[]
+ required?: boolean
+ validation?: UserAttributeValidation
+ placeholder?: string
+ display_order?: number
+ enabled?: boolean
+}
+
+export interface UserAttributeValuesMap {
+ [attributeId: number]: string
+}
diff --git a/frontend/src/views/admin/UsersView.vue b/frontend/src/views/admin/UsersView.vue
index 03bc2060..e070d08a 100644
--- a/frontend/src/views/admin/UsersView.vue
+++ b/frontend/src/views/admin/UsersView.vue
@@ -1,86 +1,289 @@
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
@@ -103,10 +306,6 @@
{{ value || '-' }}
-
- {{ value || '-' }}
-
-
+
+
+
+
+ {{ getAttributeValue(row.id, def.id) }}
+
+
+
+
{{ value }}
@@ -189,9 +404,17 @@
-
- {{ value }}
-
+
+
+
+ {{ value === 'active' ? t('common.active') : t('admin.users.disabled') }}
+
+
@@ -471,15 +694,6 @@
:placeholder="t('admin.users.enterUsername')"
/>
-
-
-
-
-
-
-
-
+
+
+
@@ -1179,6 +1390,12 @@
@confirm="confirmDelete"
@cancel="showDeleteDialog = false"
/>
+
+
+
@@ -1191,7 +1408,7 @@ import { formatDateTime } from '@/utils/format'
const { t } = useI18n()
import { adminAPI } from '@/api/admin'
-import type { User, ApiKey, Group } from '@/types'
+import type { User, ApiKey, Group, UserAttributeValuesMap, UserAttributeDefinition } from '@/types'
import type { BatchUserUsageStats } from '@/api/admin/dashboard'
import type { Column } from '@/components/common/types'
import AppLayout from '@/components/layout/AppLayout.vue'
@@ -1201,17 +1418,66 @@ import Pagination from '@/components/common/Pagination.vue'
import BaseDialog from '@/components/common/BaseDialog.vue'
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
import EmptyState from '@/components/common/EmptyState.vue'
-import Select from '@/components/common/Select.vue'
import GroupBadge from '@/components/common/GroupBadge.vue'
+import UserAttributesConfigModal from '@/components/user/UserAttributesConfigModal.vue'
+import UserAttributeForm from '@/components/user/UserAttributeForm.vue'
const appStore = useAppStore()
const { copyToClipboard: clipboardCopy } = useClipboard()
-const columns = computed(() => [
+// Generate dynamic attribute columns from enabled definitions
+const attributeColumns = computed(() =>
+ attributeDefinitions.value
+ .filter(def => def.enabled)
+ .map(def => ({
+ key: `attr_${def.id}`,
+ label: def.name,
+ sortable: false
+ }))
+)
+
+// Get formatted attribute value for display in table
+const getAttributeValue = (userId: number, attrId: number): string => {
+ const userAttrs = userAttributeValues.value[userId]
+ if (!userAttrs) return '-'
+ const value = userAttrs[attrId]
+ if (!value) return '-'
+
+ // Find definition for this attribute
+ const def = attributeDefinitions.value.find(d => d.id === attrId)
+ if (!def) return value
+
+ // Format based on type
+ if (def.type === 'multi_select' && value) {
+ try {
+ const arr = JSON.parse(value)
+ if (Array.isArray(arr)) {
+ // Map values to labels
+ return arr.map(v => {
+ const opt = def.options?.find(o => o.value === v)
+ return opt?.label || v
+ }).join(', ')
+ }
+ } catch {
+ return value
+ }
+ }
+
+ if (def.type === 'select' && value && def.options) {
+ const opt = def.options.find(o => o.value === value)
+ return opt?.label || value
+ }
+
+ return value
+}
+
+// All possible columns (for column settings)
+const allColumns = computed(() => [
{ key: 'email', label: t('admin.users.columns.user'), sortable: true },
{ key: 'username', label: t('admin.users.columns.username'), sortable: true },
- { key: 'wechat', label: t('admin.users.columns.wechat'), sortable: false },
{ key: 'notes', label: t('admin.users.columns.notes'), sortable: false },
+ // Dynamic attribute columns
+ ...attributeColumns.value,
{ key: 'role', label: t('admin.users.columns.role'), sortable: true },
{ key: 'subscriptions', label: t('admin.users.columns.subscriptions'), sortable: false },
{ key: 'balance', label: t('admin.users.columns.balance'), sortable: true },
@@ -1222,27 +1488,154 @@ const columns = computed(() => [
{ key: 'actions', label: t('admin.users.columns.actions'), sortable: false }
])
-// Filter options
-const roleOptions = computed(() => [
- { value: '', label: t('admin.users.allRoles') },
- { value: 'admin', label: t('admin.users.admin') },
- { value: 'user', label: t('admin.users.user') }
-])
+// Columns that can be toggled (exclude email and actions which are always visible)
+const toggleableColumns = computed(() =>
+ allColumns.value.filter(col => col.key !== 'email' && col.key !== 'actions')
+)
-const statusOptions = computed(() => [
- { value: '', label: t('admin.users.allStatus') },
- { value: 'active', label: t('common.active') },
- { value: 'disabled', label: t('admin.users.disabled') }
-])
+// Hidden columns (stored in Set - columns NOT in this set are visible)
+// This way, new columns are visible by default
+const hiddenColumns = reactive>(new Set())
+
+// Default hidden columns (columns hidden by default on first load)
+const DEFAULT_HIDDEN_COLUMNS = ['notes', 'subscriptions', 'usage', 'concurrency']
+
+// localStorage key for column settings
+const HIDDEN_COLUMNS_KEY = 'user-hidden-columns'
+
+// Load saved column settings
+const loadSavedColumns = () => {
+ try {
+ const saved = localStorage.getItem(HIDDEN_COLUMNS_KEY)
+ if (saved) {
+ const parsed = JSON.parse(saved) as string[]
+ parsed.forEach(key => hiddenColumns.add(key))
+ } else {
+ // Use default hidden columns on first load
+ DEFAULT_HIDDEN_COLUMNS.forEach(key => hiddenColumns.add(key))
+ }
+ } catch (e) {
+ console.error('Failed to load saved columns:', e)
+ DEFAULT_HIDDEN_COLUMNS.forEach(key => hiddenColumns.add(key))
+ }
+}
+
+// Save column settings to localStorage
+const saveColumnsToStorage = () => {
+ try {
+ localStorage.setItem(HIDDEN_COLUMNS_KEY, JSON.stringify([...hiddenColumns]))
+ } catch (e) {
+ console.error('Failed to save columns:', e)
+ }
+}
+
+// Toggle column visibility
+const toggleColumn = (key: string) => {
+ if (hiddenColumns.has(key)) {
+ hiddenColumns.delete(key)
+ } else {
+ hiddenColumns.add(key)
+ }
+ saveColumnsToStorage()
+}
+
+// Check if column is visible (not in hidden set)
+const isColumnVisible = (key: string) => !hiddenColumns.has(key)
+
+// Filtered columns based on visibility
+const columns = computed(() =>
+ allColumns.value.filter(col =>
+ col.key === 'email' || col.key === 'actions' || !hiddenColumns.has(col.key)
+ )
+)
const users = ref([])
const loading = ref(false)
const searchQuery = ref('')
+
+// Filter values (role, status, and custom attributes)
const filters = reactive({
role: '',
status: ''
})
+const activeAttributeFilters = reactive>({})
+
+// Visible filters tracking (which filters are shown in the UI)
+// Keys: 'role', 'status', 'attr_${id}'
+const visibleFilters = reactive>(new Set())
+
+// Dropdown states
+const showFilterDropdown = ref(false)
+const showColumnDropdown = ref(false)
+
+// Dropdown refs for click outside detection
+const filterDropdownRef = ref(null)
+const columnDropdownRef = ref(null)
+
+// localStorage keys
+const FILTER_VALUES_KEY = 'user-filter-values'
+const VISIBLE_FILTERS_KEY = 'user-visible-filters'
+
+// All filterable attribute definitions (enabled attributes)
+const filterableAttributes = computed(() =>
+ attributeDefinitions.value.filter(def => def.enabled)
+)
+
+// Built-in filter definitions
+const builtInFilters = computed(() => [
+ { key: 'role', name: t('admin.users.columns.role'), type: 'select' as const },
+ { key: 'status', name: t('admin.users.columns.status'), type: 'select' as const }
+])
+
+// Load saved filters from localStorage
+const loadSavedFilters = () => {
+ try {
+ // Load visible filters
+ const savedVisible = localStorage.getItem(VISIBLE_FILTERS_KEY)
+ if (savedVisible) {
+ const parsed = JSON.parse(savedVisible) as string[]
+ parsed.forEach(key => visibleFilters.add(key))
+ }
+ // Load filter values
+ const savedValues = localStorage.getItem(FILTER_VALUES_KEY)
+ if (savedValues) {
+ const parsed = JSON.parse(savedValues)
+ if (parsed.role) filters.role = parsed.role
+ if (parsed.status) filters.status = parsed.status
+ if (parsed.attributes) {
+ Object.assign(activeAttributeFilters, parsed.attributes)
+ }
+ }
+ } catch (e) {
+ console.error('Failed to load saved filters:', e)
+ }
+}
+
+// Save filters to localStorage
+const saveFiltersToStorage = () => {
+ try {
+ // Save visible filters
+ localStorage.setItem(VISIBLE_FILTERS_KEY, JSON.stringify([...visibleFilters]))
+ // Save filter values
+ const values = {
+ role: filters.role,
+ status: filters.status,
+ attributes: activeAttributeFilters
+ }
+ localStorage.setItem(FILTER_VALUES_KEY, JSON.stringify(values))
+ } catch (e) {
+ console.error('Failed to save filters:', e)
+ }
+}
+
+// Get attribute definition by ID
+const getAttributeDefinition = (attrId: number): UserAttributeDefinition | undefined => {
+ return attributeDefinitions.value.find(d => d.id === attrId)
+}
const usageStats = ref>({})
+// User attribute definitions and values
+const attributeDefinitions = ref([])
+const userAttributeValues = ref>>({})
const pagination = reactive({
page: 1,
page_size: 20,
@@ -1254,6 +1647,7 @@ const showCreateModal = ref(false)
const showEditModal = ref(false)
const showDeleteDialog = ref(false)
const showApiKeysModal = ref(false)
+const showAttributesModal = ref(false)
const submitting = ref(false)
const editingUser = ref(null)
const deletingUser = ref(null)
@@ -1317,6 +1711,14 @@ const handleClickOutside = (event: MouseEvent) => {
if (!target.closest('.action-menu-trigger') && !target.closest('.action-menu-content')) {
closeActionMenu()
}
+ // Close filter dropdown when clicking outside
+ if (filterDropdownRef.value && !filterDropdownRef.value.contains(target)) {
+ showFilterDropdown.value = false
+ }
+ // Close column dropdown when clicking outside
+ if (columnDropdownRef.value && !columnDropdownRef.value.contains(target)) {
+ showColumnDropdown.value = false
+ }
}
// Allowed groups modal state
@@ -1341,7 +1743,6 @@ const createForm = reactive({
email: '',
password: '',
username: '',
- wechat: '',
notes: '',
balance: 0,
concurrency: 1
@@ -1351,9 +1752,9 @@ const editForm = reactive({
email: '',
password: '',
username: '',
- wechat: '',
notes: '',
- concurrency: 1
+ concurrency: 1,
+ customAttributes: {} as UserAttributeValuesMap
})
const editPasswordCopied = ref(false)
@@ -1404,6 +1805,21 @@ const copyEditPassword = async () => {
}
}
+const loadAttributeDefinitions = async () => {
+ try {
+ attributeDefinitions.value = await adminAPI.userAttributes.listEnabledDefinitions()
+ } catch (e) {
+ console.error('Failed to load attribute definitions:', e)
+ }
+}
+
+// Handle attributes modal close - reload definitions and users
+const handleAttributesModalClose = async () => {
+ showAttributesModal.value = false
+ await loadAttributeDefinitions()
+ loadUsers()
+}
+
const loadUsers = async () => {
abortController?.abort()
const currentAbortController = new AbortController()
@@ -1411,13 +1827,22 @@ const loadUsers = async () => {
const { signal } = currentAbortController
loading.value = true
try {
+ // Build attribute filters from active filters
+ const attrFilters: Record = {}
+ for (const [attrId, value] of Object.entries(activeAttributeFilters)) {
+ if (value) {
+ attrFilters[Number(attrId)] = value
+ }
+ }
+
const response = await adminAPI.users.list(
pagination.page,
pagination.page_size,
{
role: filters.role as any,
status: filters.status as any,
- search: searchQuery.value || undefined
+ search: searchQuery.value || undefined,
+ attributes: Object.keys(attrFilters).length > 0 ? attrFilters : undefined
},
{ signal }
)
@@ -1428,9 +1853,10 @@ const loadUsers = async () => {
pagination.total = response.total
pagination.pages = response.pages
- // Load usage stats for all users in the list
+ // Load usage stats and attribute values for all users in the list
if (response.items.length > 0) {
const userIds = response.items.map((u) => u.id)
+ // Load usage stats
try {
const usageResponse = await adminAPI.dashboard.getBatchUsersUsage(userIds)
if (signal.aborted) {
@@ -1443,6 +1869,21 @@ const loadUsers = async () => {
}
console.error('Failed to load usage stats:', e)
}
+ // Load attribute values
+ if (attributeDefinitions.value.length > 0) {
+ try {
+ const attrResponse = await adminAPI.userAttributes.getBatchUserAttributes(userIds)
+ if (signal.aborted) {
+ return
+ }
+ userAttributeValues.value = attrResponse.attributes
+ } catch (e) {
+ if (signal.aborted) {
+ return
+ }
+ console.error('Failed to load user attribute values:', e)
+ }
+ }
}
} catch (error) {
const errorInfo = error as { name?: string; code?: string }
@@ -1478,12 +1919,54 @@ const handlePageSizeChange = (pageSize: number) => {
loadUsers()
}
+// Filter helpers
+const getAttributeDefinitionName = (attrId: number): string => {
+ const def = attributeDefinitions.value.find(d => d.id === attrId)
+ return def?.name || String(attrId)
+}
+
+// Toggle a built-in filter (role/status)
+const toggleBuiltInFilter = (key: string) => {
+ if (visibleFilters.has(key)) {
+ visibleFilters.delete(key)
+ if (key === 'role') filters.role = ''
+ if (key === 'status') filters.status = ''
+ } else {
+ visibleFilters.add(key)
+ }
+ saveFiltersToStorage()
+ loadUsers()
+}
+
+// Toggle a custom attribute filter
+const toggleAttributeFilter = (attr: UserAttributeDefinition) => {
+ const key = `attr_${attr.id}`
+ if (visibleFilters.has(key)) {
+ visibleFilters.delete(key)
+ delete activeAttributeFilters[attr.id]
+ } else {
+ visibleFilters.add(key)
+ activeAttributeFilters[attr.id] = ''
+ }
+ saveFiltersToStorage()
+ loadUsers()
+}
+
+const updateAttributeFilter = (attrId: number, value: string) => {
+ activeAttributeFilters[attrId] = value
+}
+
+// Apply filter and save to localStorage
+const applyFilter = () => {
+ saveFiltersToStorage()
+ loadUsers()
+}
+
const closeCreateModal = () => {
showCreateModal.value = false
createForm.email = ''
createForm.password = ''
createForm.username = ''
- createForm.wechat = ''
createForm.notes = ''
createForm.balance = 0
createForm.concurrency = 1
@@ -1514,9 +1997,9 @@ const handleEdit = (user: User) => {
editForm.email = user.email
editForm.password = ''
editForm.username = user.username || ''
- editForm.wechat = user.wechat || ''
editForm.notes = user.notes || ''
editForm.concurrency = user.concurrency
+ editForm.customAttributes = {}
editPasswordCopied.value = false
showEditModal.value = true
}
@@ -1525,6 +2008,7 @@ const closeEditModal = () => {
showEditModal.value = false
editingUser.value = null
editForm.password = ''
+ editForm.customAttributes = {}
editPasswordCopied.value = false
}
@@ -1536,7 +2020,6 @@ const handleUpdateUser = async () => {
const updateData: Record = {
email: editForm.email,
username: editForm.username,
- wechat: editForm.wechat,
notes: editForm.notes,
concurrency: editForm.concurrency
}
@@ -1545,6 +2028,15 @@ const handleUpdateUser = async () => {
}
await adminAPI.users.update(editingUser.value.id, updateData)
+
+ // Save custom attributes if any
+ if (Object.keys(editForm.customAttributes).length > 0) {
+ await adminAPI.userAttributes.updateUserAttributeValues(
+ editingUser.value.id,
+ editForm.customAttributes
+ )
+ }
+
appStore.showSuccess(t('admin.users.userUpdated'))
closeEditModal()
loadUsers()
@@ -1730,7 +2222,10 @@ const handleBalanceSubmit = async () => {
}
}
-onMounted(() => {
+onMounted(async () => {
+ await loadAttributeDefinitions()
+ loadSavedFilters()
+ loadSavedColumns()
loadUsers()
document.addEventListener('click', handleClickOutside)
})
diff --git a/frontend/src/views/user/ProfileView.vue b/frontend/src/views/user/ProfileView.vue
index e1b72380..27ef05e3 100644
--- a/frontend/src/views/user/ProfileView.vue
+++ b/frontend/src/views/user/ProfileView.vue
@@ -89,25 +89,6 @@
{{ user.username }}
-
-
-
{{ user.wechat }}
-