9b82fbef4e
- 添加存储配置管理页面(列表、创建、编辑) - 支持本地存储、S3、WebDAV、FTP、SFTP 等存储类型 - 添加存储配置测试连接功能 - 本地存储自动初始化且禁止删除 - 修复 Switch 组件状态显示问题 - 添加更新存储配置时的 status 字段支持
188 lines
23 KiB
Go
188 lines
23 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"log"
|
|
|
|
"verification-platform-backend/internal/database"
|
|
"verification-platform-backend/internal/model"
|
|
|
|
"gorm.io/gorm/clause"
|
|
)
|
|
|
|
func main() {
|
|
database.Init()
|
|
db := database.DB
|
|
|
|
docTranslations := map[string]struct {
|
|
TitleEn string
|
|
ContentEn string
|
|
SummaryEn string
|
|
}{
|
|
"api-heartbeat": {
|
|
TitleEn: "Heartbeat Verification",
|
|
ContentEn: "# Heartbeat Verification\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/heartbeat\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"success\"\n}\n```\n\n### Notes\n- Keeps user online status\n- Recommended to call every 30 seconds\n- User will be considered offline if heartbeat is not called for a certain period\n- JWT token must be included in the request header",
|
|
SummaryEn: "Heartbeat verification instructions",
|
|
},
|
|
"api-recharge": {
|
|
TitleEn: "Card Key Recharge",
|
|
ContentEn: "# Card Key Recharge\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/recharge\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | User account |\n| card_key | string | Yes | Card key |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"card_key\": \"VIP123456\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Recharge successful\",\n \"data\": {\n \"value\": 30\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| value | number | Recharge value (meaning varies by card type) |\n\n### Error Response\n```json\n{\n \"code\": 400,\n \"message\": \"Invalid or expired card key\"\n}\n```",
|
|
SummaryEn: "Card key recharge instructions",
|
|
},
|
|
"api-devices": {
|
|
TitleEn: "Get Bound Device List",
|
|
ContentEn: "# Get Bound Device List\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/devices\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"devices\": [\n {\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"bind_time\": \"2024-03-04T12:00:00Z\",\n \"last_active\": \"2024-03-04T13:00:00Z\"\n }\n ]\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| device_id | string | Device ID |\n| device_name | string | Device name |\n| bind_time | string | Bind time |\n| last_active | string | Last active time |",
|
|
SummaryEn: "Get bound device list instructions",
|
|
},
|
|
"api-unbind-device": {
|
|
TitleEn: "Unbind Device",
|
|
ContentEn: "# Unbind Device\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| device_id | string | Yes | Device ID |\n\n### Request Example\n```json\n{\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Unbind successful\"\n}\n```\n\n### Error Response\n```json\n{\n \"code\": 400,\n \"message\": \"Device not found or not bound\"\n}\n```",
|
|
SummaryEn: "Unbind device instructions",
|
|
},
|
|
"api-unbind-device-with-auth": {
|
|
TitleEn: "Unbind Device with Authentication",
|
|
ContentEn: "# Unbind Device with Authentication\n\nWhen users cannot login due to device limit, they can use this endpoint to unbind a device with username and password.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/unbind-device-with-auth\n```\n\n### Note\nThis endpoint does not require Authorization header, uses username and password for authentication.\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | Username |\n| password | string | Yes | Password |\n| device_id | string | Yes | Device ID to unbind |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"password\": \"password123\",\n \"device_id\": \"device_001\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Unbind successful\"\n }\n}\n```",
|
|
SummaryEn: "Unbind device with authentication instructions",
|
|
},
|
|
"api-reset-password": {
|
|
TitleEn: "Reset Password (Email Verification)",
|
|
ContentEn: "# Reset Password (Email Verification)\n\nWhen users forget their password, they can reset it via email verification code.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/reset-password\n```\n\n### Note\nThis endpoint does not require Authorization header, must call send email verification code endpoint first.\n\n### Prerequisites\n- Application has password reset enabled\n- User's package supports password reset\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| email | string | Yes | Email address |\n| code | string | Yes | Email verification code |\n| password | string | Yes | New password (at least 6 characters) |\n\n### Request Example\n```json\n{\n \"email\": \"user@example.com\",\n \"code\": \"123456\",\n \"password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Password reset successful\"\n }\n}\n```",
|
|
SummaryEn: "Reset password via email verification code",
|
|
},
|
|
"api-change-password": {
|
|
TitleEn: "Change Password",
|
|
ContentEn: "# Change Password\n\nChange password by verifying username and original password.\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/change-password\n```\n\n### Note\nThis endpoint does not require Authorization header, uses username and original password for authentication.\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| username | string | Yes | Username |\n| old_password | string | Yes | Original password |\n| new_password | string | Yes | New password (at least 6 characters) |\n\n### Request Example\n```json\n{\n \"username\": \"user123\",\n \"old_password\": \"oldpassword123\",\n \"new_password\": \"newpassword123\"\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"message\": \"Password changed successfully\"\n }\n}\n```",
|
|
SummaryEn: "Change password",
|
|
},
|
|
"api-device-count": {
|
|
TitleEn: "Get Device Count",
|
|
ContentEn: "# Get Device Count\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/device-count\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"count\": 3,\n \"max_devices\": 5\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| count | number | Current bound device count |\n| max_devices | number | Maximum allowed devices |",
|
|
SummaryEn: "Get device count instructions",
|
|
},
|
|
"api-app-info": {
|
|
TitleEn: "Get Application Info",
|
|
ContentEn: "# Get Application Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/info\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"app_id\": 1,\n \"name\": \"My App\",\n \"description\": \"Game verification app\",\n \"status\": \"active\"\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| app_id | number | Application ID |\n| name | string | Application name |\n| description | string | Application description |\n| status | string | Application status |",
|
|
SummaryEn: "Get application info instructions",
|
|
},
|
|
"api-account": {
|
|
TitleEn: "Get User Account Info",
|
|
ContentEn: "# Get User Account Info\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/account\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"user_id\": 123,\n \"username\": \"user123\",\n \"expire_time\": \"2024-04-04T00:00:00Z\",\n \"remaining_days\": 30\n }\n}\n```\n\n### Response Fields\n| Field | Type | Description |\n|--------|------|------|\n| user_id | number | User ID |\n| username | string | Username |\n| expire_time | string | Expiration time |\n| remaining_days | number | Remaining days |",
|
|
SummaryEn: "Get user account info instructions",
|
|
},
|
|
"api-constants": {
|
|
TitleEn: "Get Application Constants",
|
|
ContentEn: "# Get Application Constants\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"name\": \"Constant Name\",\n \"key\": \"CONSTANT_KEY\",\n \"value\": \"Constant Value\",\n \"var_type\": \"string\",\n \"description\": \"Constant description\"\n }\n ]\n}\n```",
|
|
SummaryEn: "Get application constants instructions",
|
|
},
|
|
"api-constant": {
|
|
TitleEn: "Get Specific Constant",
|
|
ContentEn: "# Get Specific Constant\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/constants/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| key | string | Yes | Constant key |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"id\": 1,\n \"name\": \"Constant Name\",\n \"key\": \"CONSTANT_KEY\",\n \"value\": \"Constant Value\",\n \"var_type\": \"string\",\n \"description\": \"Constant description\"\n }\n}\n```",
|
|
SummaryEn: "Get specific constant instructions",
|
|
},
|
|
"api-variables": {
|
|
TitleEn: "Get Application Variables",
|
|
ContentEn: "# Get Application Variables\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"name\": \"Variable Name\",\n \"key\": \"VARIABLE_KEY\",\n \"default_value\": \"Default Value\",\n \"var_type\": \"string\",\n \"scope\": \"user\",\n \"description\": \"Variable description\"\n }\n ]\n}\n```",
|
|
SummaryEn: "Get application variables instructions",
|
|
},
|
|
"api-variable": {
|
|
TitleEn: "Get Specific Variable",
|
|
ContentEn: "# Get Specific Variable\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/variables/:key\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| key | string | Yes | Variable key |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"id\": 1,\n \"name\": \"Variable Name\",\n \"key\": \"VARIABLE_KEY\",\n \"default_value\": \"Default Value\",\n \"var_type\": \"string\",\n \"scope\": \"user\",\n \"description\": \"Variable description\"\n }\n}\n```",
|
|
SummaryEn: "Get specific variable instructions",
|
|
},
|
|
"api-update-variables": {
|
|
TitleEn: "Update User Variables",
|
|
ContentEn: "# Update User Variables\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/variables\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| variables | object | Yes | Variable key-value pairs |\n\n### Request Example\n```json\n{\n \"variables\": {\n \"nickname\": \"New Nickname\",\n \"level\": 10\n }\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Update successful\"\n}\n```\n\n### Notes\n- Only user-scoped variables can be updated\n- Variable value types must match the defined types",
|
|
SummaryEn: "Update user variables instructions",
|
|
},
|
|
"api-call-function": {
|
|
TitleEn: "Call Cloud Function",
|
|
ContentEn: "# Call Cloud Function\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/call-function\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| function_name | string | Yes | Cloud function name |\n| params | object | No | Parameters to pass to the function |\n\n### Request Example\n```json\n{\n \"function_name\": \"calculatePrice\",\n \"params\": {\n \"x\": 10,\n \"y\": 20\n }\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Success\",\n \"data\": {\n \"result\": 30,\n \"execution_time\": 0.001234\n }\n}\n```",
|
|
SummaryEn: "Call cloud function instructions",
|
|
},
|
|
"api-check-update": {
|
|
TitleEn: "Check for Updates",
|
|
ContentEn: "# Check for Updates\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/check-update\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| version | string | No | Current client version |\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": {\n \"has_update\": true,\n \"latest_version\": \"2.0.0\",\n \"download_url\": \"https://example.com/download/v2.0.0\",\n \"update_notes\": \"Fixed several bugs\",\n \"update_strategy\": \"optional\",\n \"update_method\": \"manual\"\n }\n}\n```",
|
|
SummaryEn: "Check for updates instructions",
|
|
},
|
|
"api-announcements": {
|
|
TitleEn: "Get Announcements",
|
|
ContentEn: "# Get Announcements\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/announcements\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"title\": \"System Maintenance Notice\",\n \"content\": \"System will be under maintenance tonight...\",\n \"created_at\": \"2024-03-04T10:00:00Z\"\n }\n ]\n}\n```",
|
|
SummaryEn: "Get announcements instructions",
|
|
},
|
|
"api-instances": {
|
|
TitleEn: "Get Online Instance List",
|
|
ContentEn: "# Get Online Instance List\n\n### Endpoint\n```\nGET /api/v1/app/:appKey/instances\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| user_id | number | Yes | User ID |\n\n### Request Example\n```json\n{\n \"user_id\": 123\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"data\": [\n {\n \"id\": 1,\n \"instance_id\": \"instance_001\",\n \"device_id\": \"device_001\",\n \"device_name\": \"My Computer\",\n \"is_online\": true,\n \"last_heartbeat\": \"2024-03-04T13:00:00Z\",\n \"created_at\": \"2024-03-04T12:00:00Z\"\n }\n ]\n}\n```",
|
|
SummaryEn: "Get online instance list instructions",
|
|
},
|
|
"api-force-offline": {
|
|
TitleEn: "Force Instance Offline",
|
|
ContentEn: "# Force Instance Offline\n\n### Endpoint\n```\nPOST /api/v1/app/:appKey/instances/:instance_id/offline\n```\n\n### Request Header\n```\nAuthorization: Bearer {token}\n```\n\n### Path Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| instance_id | string | Yes | Instance identifier |\n\n### Request Parameters\n| Parameter | Type | Required | Description |\n|--------|------|------|------|\n| user_id | number | Yes | User ID |\n\n### Request Example\n```json\n{\n \"user_id\": 123\n}\n```\n\n### Response Example\n```json\n{\n \"code\": 200,\n \"message\": \"Forced offline successfully\"\n}\n```",
|
|
SummaryEn: "Force instance offline instructions",
|
|
},
|
|
"example-software": {
|
|
TitleEn: "Software Verification Example",
|
|
ContentEn: "# Software Verification Example\n\n## Complete Verification Flow\n\n### 1. User Input Username and Password\n\n```javascript\nconst username = document.getElementById('username').value;\nconst password = document.getElementById('password').value;\n```\n\n### 2. Get Device ID\n\n```javascript\nfunction getDeviceId() {\n let deviceId = localStorage.getItem('device_id');\n if (!deviceId) {\n deviceId = 'device_' + Math.random().toString(36).substr(2, 9);\n localStorage.setItem('device_id', deviceId);\n }\n return deviceId;\n}\n\nconst deviceId = getDeviceId();\n```\n\n### 3. Call Login API\n\n```javascript\nconst response = await fetch(`/api/v1/app/${appKey}/login`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({\n username,\n password,\n device_id: deviceId\n })\n});\n\nconst result = await response.json();\n\nif (result.code !== 200) {\n alert('Login failed: ' + result.message);\n return;\n}\n```\n\n### 4. Save Token\n\n```javascript\nlocalStorage.setItem('token', result.data.token);\nlocalStorage.setItem('expire_time', result.data.expire_time);\n```\n\n### 5. Start Heartbeat\n\n```javascript\nsetInterval(async () => {\n await fetch(`/api/v1/app/${appKey}/heartbeat`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${localStorage.getItem('token')}`\n },\n body: JSON.stringify({\n device_id: deviceId\n })\n });\n}, 30000);\n```\n\n### 6. Enter Main Program\n\n```javascript\nstartApplication();\n```",
|
|
SummaryEn: "Complete software login verification example",
|
|
},
|
|
"example-game": {
|
|
TitleEn: "Game Verification Example",
|
|
ContentEn: "# Game Verification Example\n\n## Unity Integration\n\n### 1. Call Login API\n\n```csharp\nusing UnityEngine;\nusing UnityEngine.Networking;\n\npublic class AuthManager : MonoBehaviour {\n private string appKey = \"your_app_key\";\n private string token;\n private string deviceId;\n \n void Start() {\n deviceId = SystemInfo.deviceUniqueIdentifier;\n }\n \n public async void Login(string username, string password) {\n string url = $\"https://api.example.com/api/v1/app/{appKey}/login\";\n \n string jsonData = $\"{{\\\"username\\\": \\\"{username}\\\", \\\"password\\\": \\\"{password}\\\", \\\"device_id\\\": \\\"{deviceId}\\\"}}\";\n \n using (UnityWebRequest request = UnityWebRequest.Post(url, jsonData)) {\n request.SetRequestHeader(\"Content-Type\", \"application/json\");\n \n yield return request.SendWebRequest();\n \n if (request.result == UnityWebRequest.Result.Success) {\n string response = request.downloadHandler.text;\n var result = JsonUtility.FromJson<LoginResponse>(response);\n \n if (result.code == 200) {\n token = result.data.token;\n PlayerPrefs.SetString(\"token\", token);\n \n // Start heartbeat\n StartCoroutine(HeartbeatCoroutine());\n \n // Enter game\n LoadMainScene();\n } else {\n Debug.LogError(\"Login failed: \" + result.message);\n }\n } else {\n Debug.LogError(\"Request failed: \" + request.error);\n }\n }\n }\n}\n```",
|
|
SummaryEn: "Unity game integration example",
|
|
},
|
|
"faq-api-key": {
|
|
TitleEn: "How to Get API Key?",
|
|
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to admin dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
|
|
SummaryEn: "Detailed steps to get API key",
|
|
},
|
|
"faq-card-fail": {
|
|
TitleEn: "What to Do if Card Key Verification Fails?",
|
|
ContentEn: "# What to Do if Card Key Verification Fails?\n\n## Common Reasons\n\n1. **Card Key Expired**\n - Check the validity period of the card key\n - Expired card keys cannot be used\n\n2. **Card Key Already Used**\n - Single-use card keys can only be verified once\n - Used card keys cannot be verified again\n\n3. **Device Binding Error**\n - Check if device ID is correct\n - Confirm if application has device binding enabled\n\n4. **Application Configuration Error**\n - Check if AppID and AppKey are correct\n - Confirm if application status is normal\n\n## Solutions\n\n1. Contact application customer service\n2. Get a new card key\n3. Check device network connection\n4. View application logs for detailed error information",
|
|
SummaryEn: "Common reasons and solutions for card key verification failure",
|
|
},
|
|
"faq-agent": {
|
|
TitleEn: "How to Implement Agent Authorization?",
|
|
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows admins to authorize their applications to other admins, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Revenue**\n - Sales revenue is settled proportionally\n - Authorized party receives income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
|
|
SummaryEn: "Implementation process and permission management for agent authorization",
|
|
},
|
|
}
|
|
|
|
var categories []model.DocCategory
|
|
if err := db.Find(&categories).Error; err != nil {
|
|
log.Fatal("Failed to get categories:", err)
|
|
}
|
|
|
|
for i := range categories {
|
|
translation, ok := docTranslations[categories[i].Slug]
|
|
if ok {
|
|
categories[i].NameEn = translation.TitleEn
|
|
categories[i].DescriptionEn = translation.ContentEn
|
|
db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{"name_en", "description_en"}),
|
|
}).Create(&categories[i])
|
|
fmt.Printf("Updated category: %s -> %s\n", categories[i].Name, categories[i].NameEn)
|
|
}
|
|
}
|
|
|
|
var docs []model.Doc
|
|
if err := db.Find(&docs).Error; err != nil {
|
|
log.Fatal("Failed to get docs:", err)
|
|
}
|
|
|
|
for i := range docs {
|
|
translation, ok := docTranslations[docs[i].Slug]
|
|
if ok {
|
|
docs[i].TitleEn = translation.TitleEn
|
|
docs[i].ContentEn = translation.ContentEn
|
|
docs[i].SummaryEn = translation.SummaryEn
|
|
db.Clauses(clause.OnConflict{
|
|
Columns: []clause.Column{{Name: "id"}},
|
|
DoUpdates: clause.AssignmentColumns([]string{"title_en", "content_en", "summary_en"}),
|
|
}).Create(&docs[i])
|
|
fmt.Printf("Updated doc: %s -> %s\n", docs[i].Title, docs[i].TitleEn)
|
|
}
|
|
}
|
|
|
|
fmt.Println("\nDocument translations updated successfully!")
|
|
}
|