feat(deploy): production deployment setup and fixes

- Debian-based api image (bookworm-slim), docker/debian mirrors, prisma
  binaryTargets for openssl 3.0
- nginx: admin SPA under /admin, TLS via acme.sh (ZeroSSL) + auto-renewal
  cron, http->https redirect
- prisma: add origin_goods.delisted migration, sync missing schema
  (good_image/tag_font_color/good_tags), fix users.createdAt Timestamptz
- api: CORS wildcard reflection, helmet CORP cross-origin, price
  backfill in persistProductDetail, categoryIcon ancestor fallback,
  mediaByColor per-color gallery in public goods detail
- admin: /admin base path (vite + router)
- import-data.mjs: udt_name casting, serial sequence advance fix
This commit is contained in:
yeuimu
2026-08-26 14:23:09 +08:00
parent be0b90e68f
commit 6c61a4e871
982 changed files with 74156 additions and 179393 deletions
@@ -1,124 +0,0 @@
# Install Plugins Before Mounting the App
## Rule
All plugins must be installed using `app.use()` BEFORE calling `app.mount()`. Installing plugins after the app is mounted can lead to reactivity issues, missing dependencies, and unexpected behavior.
## Why This Matters
1. **Hidden dependencies**: Components may render before plugins they depend on are available, causing runtime errors.
2. **Reactivity issues**: Late plugin installation can cause subtle reactivity problems where provided values aren't properly reactive.
3. **Initialization order**: Many plugins (like vue-router, pinia) need to set up state before any component renders.
4. **Ecosystem complexity**: Adding plugins after mount can cause issues with Vue's internal ecosystem and hydration in SSR scenarios.
## Bad Practice
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import i18nPlugin from './plugins/i18n'
const app = createApp(App)
// Mounting first - plugins not yet available!
app.mount('#app')
// Installing after mount - TOO LATE!
app.use(router)
app.use(i18nPlugin, { locale: 'en' })
```
## Good Practice
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { createPinia } from 'pinia'
import i18nPlugin from './plugins/i18n'
const app = createApp(App)
// Install all plugins BEFORE mounting
app.use(createPinia())
app.use(router)
app.use(i18nPlugin, { locale: 'en' })
// Mount LAST
app.mount('#app')
```
## Plugin Installation Order
The order of `app.use()` calls can matter when plugins depend on each other:
```typescript
const app = createApp(App)
// 1. State management first (other plugins might need it)
app.use(createPinia())
// 2. Router (may depend on state)
app.use(router)
// 3. Other plugins (may depend on router or state)
app.use(authPlugin)
app.use(i18nPlugin, { locale: 'en' })
// 4. Mount last
app.mount('#app')
```
## Async Plugin Installation
If you need to perform async operations before mounting:
```typescript
import { createApp } from 'vue'
import App from './App.vue'
import { loadPlugins } from './plugins'
async function bootstrap() {
const app = createApp(App)
// Await async plugin setup
const i18nPlugin = await loadI18nMessages()
// Install all plugins
app.use(i18nPlugin)
// Mount after everything is ready
app.mount('#app')
}
bootstrap()
```
## Duplicate Installation Protection
Vue's `app.use()` automatically prevents duplicate plugin installation:
```typescript
app.use(myPlugin)
app.use(myPlugin) // This second call is ignored - no double installation
// This is handled internally by Vue, providing a safety net
```
## Common Symptoms of Late Plugin Installation
- `inject()` returns `undefined` unexpectedly
- Router navigation guards not firing
- Store state not reactive
- Template errors about undefined global properties
- Hydration mismatches in SSR
## References
- [Vue.js Plugins Documentation](https://vuejs.org/guide/reusability/plugins.html)
- [Vue.js Application API](https://vuejs.org/api/application.html)
- [Vue 3 Migration Guide - Global API](https://v3-migration.vuejs.org/breaking-changes/global-api.html)