Add environment variables for presets and local storage configuration (#167)
This commit is contained in:
parent
2fe35fafcc
commit
c5a9bf91c8
@ -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_DEFAULT_PRESET=plain
|
||||
VITE_QR_CODE_PRESETS=
|
||||
VITE_FRAME_PRESET=
|
||||
VITE_FRAME_PRESETS=
|
||||
VITE_DISABLE_LOCAL_STORAGE=false
|
@ -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.
|
||||
|
||||
|
26
README.md
26
README.md
@ -138,32 +138,42 @@ docker run -d -p 8081:8080 mini-qr
|
||||
|
||||
### Customization
|
||||
|
||||
- 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 | `/` |
|
||||
| `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 `BASE_PATH=/your-path` to deploy the app under a subdirectory (e.g., for hosting at `domain.com/your-path`).
|
||||
- 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
|
||||
|
@ -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
7
env.d.ts
vendored
@ -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 {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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]
|
Loading…
x
Reference in New Issue
Block a user