Add environment variables for presets and local storage configuration (#167)

This commit is contained in:
Estee Tey 2025-06-08 09:58:50 +08:00 committed by GitHub
parent 2fe35fafcc
commit c5a9bf91c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 117 additions and 31 deletions

View File

@ -8,6 +8,9 @@ CROWDIN_PROJECT_ID="your_project_id"
# Default: /
BASE_PATH=/
# Hide credits in the footer
# Default: false
VITE_HIDE_CREDITS=false
VITE_HIDE_CREDITS=false
VITE_DEFAULT_PRESET=plain
VITE_QR_CODE_PRESETS=
VITE_FRAME_PRESET=
VITE_FRAME_PRESETS=
VITE_DISABLE_LOCAL_STORAGE=false

View File

@ -83,7 +83,7 @@ The project is a modern Vite-powered Vue.js 3 application with TypeScript suppor
**Utilities (`src/utils/`):**
- `presets.ts`: QR code style presets and preset management
- `qrCodePresets.ts`: QR code style presets and preset management
- `dataEncoding.ts`: Data encoding/decoding utilities for different QR code types
- `convertToImage.ts`: Image conversion and export utilities (PNG, JPG, SVG)
- `csv.ts`: CSV parsing and validation for batch operations
@ -210,7 +210,7 @@ const yourNewPreset = {
}
```
Then add it to the `allPresets` array in `src/utils/presets.ts`. New presets should always be added as the last item in the array.
Then add it to the `allQrCodePresets` array in `src/utils/qrCodePresets.ts`. New presets should always be added as the last item in the array.
You will then see your new preset in the Presets dropdown in the website.

View File

@ -138,32 +138,42 @@ docker run -d -p 8081:8080 mini-qr
### Customization
#### Environment Variables
| Variable | Description | Default |
| ---------------------------- | ---------------------------------------------------------------------------------- | --------- |
| `BASE_PATH` | Base path for deployment | `/` |
| `VITE_HIDE_CREDITS` | Set to `"true"` to hide credits in the footer | `"false"` |
| `VITE_DEFAULT_PRESET` | Name of the default QR code preset to load (e.g., `"lyqht"`) | `""` |
| `VITE_QR_CODE_PRESETS` | JSON string defining custom QR code presets. E.g., `'[{"name":"c1","data":"hi"}]'` | `"[]"` |
| `VITE_FRAME_PRESET` | Name of the default frame preset to load (e.g., `"default"`) | `""` |
| `VITE_FRAME_PRESETS` | JSON string defining custom frame presets. E.g., `'[{"name":"fA","text":"QR"}]'` | `"[]"` |
| `VITE_DISABLE_LOCAL_STORAGE` | Set to `"true"` to disable loading saved settings from local storage on startup | `"false"` |
### Docker configuration
- You can edit `nginx.conf` or mount your own static files by uncommenting the `volumes` section in `docker-compose.yml`.
- The production image uses Nginx for optimal static file serving.
- The `.dockerignore` file is included for smaller, faster builds.
- Set `HIDE_CREDITS=1` to remove the maintainer credit from the footer.
- Set `BASE_PATH=/your-path` to deploy the app under a subdirectory (e.g., for hosting at `domain.com/your-path`).
#### Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `HIDE_CREDITS` | Hide credits in the footer | `false` |
| `BASE_PATH` | Base path for deployment | `/` |
- If you want to have a default preset to be fixed, you should set `VITE_DISABLE_LOCAL_STORAGE=true`
#### Examples
Deploy at root path (default):
```bash
docker compose up -d
```
Deploy at subdirectory `/mini-qr`:
```bash
BASE_PATH=/mini-qr docker compose up -d
```
For custom builds with specific BASE_PATH:
```bash
docker build --build-arg BASE_PATH=/mini-qr -t mini-qr .
docker run -d -p 8081:8080 mini-qr

View File

@ -7,6 +7,11 @@ services:
environment:
- VITE_HIDE_CREDITS=${HIDE_CREDITS:-false}
- BASE_PATH=${BASE_PATH:-/}
- VITE_DEFAULT_PRESET=${DEFAULT_PRESET:-}
- VITE_QR_CODE_PRESETS=${PRESETS:-}
- VITE_FRAME_PRESET=${FRAME_PRESET:-}
- VITE_FRAME_PRESETS=${FRAME_PRESETS:-}
- VITE_DISABLE_LOCAL_STORAGE=${DISABLE_LOCAL_STORAGE:-false}
# Uncomment the following lines to build locally instead of pulling from ghcr.io
build:
context: .

7
env.d.ts vendored
View File

@ -1,8 +1,13 @@
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_HIDE_CREDITS?: string
readonly BASE_PATH?: string
readonly VITE_HIDE_CREDITS?: string
readonly VITE_DEFAULT_PRESET?: string
readonly VITE_QR_CODE_PRESETS?: string
readonly VITE_FRAME_PRESET?: string
readonly VITE_FRAME_PRESETS?: string
readonly VITE_DISABLE_LOCAL_STORAGE?: string
}
interface ImportMeta {

View File

@ -26,8 +26,8 @@ import {
import { parseCSV, validateCSVData } from '@/utils/csv'
import { generateVCardData } from '@/utils/dataEncoding'
import { getNumericCSSValue } from '@/utils/formatting'
import { allPresets, type Preset } from '@/utils/presets'
import { allFramePresets, type FramePreset } from '@/utils/framePresets'
import { allQrCodePresets, defaultPreset, type Preset } from '@/utils/qrCodePresets'
import { allFramePresets, defaultFramePreset, type FramePreset } from '@/utils/framePresets'
import { useMediaQuery } from '@vueuse/core'
import JSZip from 'jszip'
import {
@ -65,7 +65,6 @@ const { t } = useI18n()
//#endregion
//#region /* QR code style settings */
const defaultPreset = allPresets[0]
const data = ref(props.initialData || '')
const debouncedData = ref(data.value)
let dataDebounceTimer: ReturnType<typeof setTimeout>
@ -207,8 +206,8 @@ function uploadImage() {
const isPresetSelectOpen = ref(false)
const allPresetOptions = computed(() => {
const options = lastCustomLoadedPreset.value
? [lastCustomLoadedPreset.value, ...allPresets]
: allPresets
? [lastCustomLoadedPreset.value, ...allQrCodePresets]
: allQrCodePresets
return options.map((preset) => ({ value: preset.name, label: t(preset.name) }))
})
const selectedPreset = ref<
@ -244,19 +243,29 @@ watch(selectedPreset, () => {
const LAST_LOADED_LOCALLY_PRESET_KEY = 'Last saved locally'
const LOADED_FROM_FILE_PRESET_KEY = 'Loaded from file'
const CUSTOM_LOADED_PRESET_KEYS = [LAST_LOADED_LOCALLY_PRESET_KEY, LOADED_FROM_FILE_PRESET_KEY]
const selectedPresetKey = ref<string>(LAST_LOADED_LOCALLY_PRESET_KEY)
const selectedPresetKey = ref<string>(
import.meta.env.VITE_DISABLE_LOCAL_STORAGE === 'true'
? defaultPreset.name
: localStorage.getItem('qrCodeConfig')
? LAST_LOADED_LOCALLY_PRESET_KEY
: defaultPreset.name
)
const lastCustomLoadedPreset = ref<Preset>()
watch(
selectedPresetKey,
(newKey, prevKey) => {
if (newKey === prevKey || !newKey) return
if (CUSTOM_LOADED_PRESET_KEYS.includes(newKey) && lastCustomLoadedPreset.value) {
if (
import.meta.env.VITE_DISABLE_LOCAL_STORAGE !== 'true' &&
CUSTOM_LOADED_PRESET_KEYS.includes(newKey) &&
lastCustomLoadedPreset.value
) {
selectedPreset.value = lastCustomLoadedPreset.value
return
}
const updatedPreset = allPresets.find((preset) => preset.name === newKey)
const updatedPreset = allQrCodePresets.find((preset) => preset.name === newKey)
if (updatedPreset) {
selectedPreset.value = updatedPreset
}
@ -301,7 +310,6 @@ const frameStyle = ref<FrameStyle>({
borderRadius: '8px',
padding: '16px'
})
const defaultFramePreset = allFramePresets[0]
const selectedFramePresetKey = ref<string>(defaultFramePreset.name)
const lastCustomLoadedFramePreset = ref<FramePreset>()
const CUSTOM_LOADED_FRAME_PRESET_KEYS = [
@ -327,7 +335,11 @@ watch(
(newKey, prevKey) => {
if (newKey === prevKey || !newKey) return
if (CUSTOM_LOADED_FRAME_PRESET_KEYS.includes(newKey) && lastCustomLoadedFramePreset.value) {
if (
import.meta.env.VITE_DISABLE_LOCAL_STORAGE !== 'true' &&
CUSTOM_LOADED_FRAME_PRESET_KEYS.includes(newKey) &&
lastCustomLoadedFramePreset.value
) {
applyFramePreset(lastCustomLoadedFramePreset.value)
return
}
@ -588,7 +600,23 @@ watch(
)
onMounted(() => {
loadQRConfigFromLocalStorage()
if (import.meta.env.VITE_DISABLE_LOCAL_STORAGE !== 'true') {
const qrCodeConfigString = localStorage.getItem('qrCodeConfig')
if (qrCodeConfigString) {
loadQRConfig(qrCodeConfigString, LAST_LOADED_LOCALLY_PRESET_KEY)
} else {
// No localStorage data found, use the environment variable default preset
selectedPreset.value = { ...defaultPreset }
selectedPresetKey.value = defaultPreset.name
}
// No separate frameConfig loading from localStorage noted,
// assuming selectedFramePresetKey watcher handles it if lastCustomLoadedFramePreset was populated by loadQRConfig
}
// Set initial data if provided through props
if (props.initialData) {
data.value = props.initialData
}
})
//#endregion

View File

@ -14,7 +14,7 @@ export interface FramePreset {
position?: 'top' | 'bottom' | 'left' | 'right'
}
export const defaultFramePreset: FramePreset = {
export const plainFramePreset: FramePreset = {
name: 'Default Frame',
style: {
textColor: '#000000',
@ -50,8 +50,24 @@ export const borderlessFramePreset: FramePreset = {
}
}
export const allFramePresets: FramePreset[] = [
defaultFramePreset,
export const builtInFramePresets: FramePreset[] = [
plainFramePreset,
darkFramePreset,
borderlessFramePreset
]
function parseFramePresetsFromEnv(envVal?: string): FramePreset[] | undefined {
if (!envVal) return undefined
try {
return JSON.parse(envVal) as FramePreset[]
} catch (err) {
console.error('Failed to parse VITE_FRAME_PRESETS', err)
return undefined
}
}
const envFramePresets = parseFramePresetsFromEnv(import.meta.env.VITE_FRAME_PRESETS)
export const allFramePresets: FramePreset[] = envFramePresets ?? builtInFramePresets
export const defaultFramePreset: FramePreset =
allFramePresets.find((p) => p.name === import.meta.env.VITE_FRAME_PRESET) ?? allFramePresets[0]

View File

@ -244,7 +244,7 @@ export const vueJsPreset: Preset = {
// Individual presets
export const defaultPreset: Preset = {
export const lyqhtPreset: Preset = {
...defaultPresetOptions,
name: 'Default (lyqht)',
data: 'https://github.com/lyqht',
@ -316,8 +316,8 @@ export const hackomania2025Preset = {
style: Hackomania2025Config.style
} as Preset
export const allPresets: Preset[] = [
defaultPreset,
export const builtInPresets: Preset[] = [
lyqhtPreset,
plainPreset,
...[
padletPreset,
@ -336,3 +336,22 @@ export const allPresets: Preset[] = [
hackomania2025Preset
].sort((a, b) => a.name.localeCompare(b.name))
]
function parsePresetsFromEnv(envVal?: string): Preset[] | undefined {
if (!envVal) return undefined
try {
return JSON.parse(envVal) as Preset[]
} catch (err) {
console.error('Failed to parse VITE_QR_CODE_PRESETS', err)
return undefined
}
}
const envPresets = parsePresetsFromEnv(import.meta.env.VITE_QR_CODE_PRESETS)
export const allQrCodePresets: Preset[] = envPresets ?? builtInPresets
export const defaultPreset: Preset =
import.meta.env.VITE_DEFAULT_PRESET
? allQrCodePresets.find((p) => p.name === import.meta.env.VITE_DEFAULT_PRESET) ??
allQrCodePresets[0]
: allQrCodePresets[0]