Add new features: DefaultBrowser, DynamicLock, SandwichReminder
- Implemented DefaultBrowser feature to notify users when the default browser does not match the configured app. - Added DynamicLock feature to disable Dynamic Lock while connected to a specific network and re-enable it after disconnecting. - Created SandwichReminder feature to prompt users to order a sandwich during work hours based on network and time settings. Introduced helper libraries for configuration, elevation, logging, network utilities, and toast notifications. - Config.ps1: Added functions for reading and writing configuration and state files. - Elevation.ps1: Added functions to check for administrator privileges and request elevation. - Logging.ps1: Implemented a shared logging utility for consistent logging across features. - NetworkUtils.ps1: Added a function to check for DNS suffix connectivity. - ToastHelper.ps1: Created a helper for displaying Windows toast notifications. Implemented runner.ps1 as the main entry point for executing features based on configuration.
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
# Config.ps1 — config.json and state.json read/write helpers
|
||||
# Requires $InternalRoot to be defined in the calling script's scope before dot-sourcing.
|
||||
|
||||
function Get-ConfigPath { Join-Path $InternalRoot 'data\config.json' }
|
||||
function Get-StatePath { Join-Path $InternalRoot 'data\state\state.json' }
|
||||
|
||||
# ── Config ────────────────────────────────────────────────────────────────────
|
||||
|
||||
function Get-Config {
|
||||
$path = Get-ConfigPath
|
||||
if (-not (Test-Path $path)) {
|
||||
return @{ features = @{} }
|
||||
}
|
||||
try {
|
||||
$raw = Get-Content -Path $path -Raw -Encoding UTF8 -ErrorAction Stop
|
||||
return ConvertTo-DeepHashtable ($raw | ConvertFrom-Json)
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to read config.json, returning empty defaults: $_" -Feature 'Config'
|
||||
return @{ features = @{} }
|
||||
}
|
||||
}
|
||||
|
||||
function Save-Config {
|
||||
param([hashtable]$Config)
|
||||
|
||||
$path = Get-ConfigPath
|
||||
$dir = Split-Path $path -Parent
|
||||
if (-not (Test-Path $dir)) {
|
||||
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||
}
|
||||
try {
|
||||
$Config | ConvertTo-Json -Depth 10 | Set-Content -Path $path -Encoding UTF8 -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to save config.json: $_" -Feature 'Config'
|
||||
}
|
||||
}
|
||||
|
||||
# ── State ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
function Get-State {
|
||||
$path = Get-StatePath
|
||||
if (-not (Test-Path $path)) {
|
||||
return @{}
|
||||
}
|
||||
try {
|
||||
$raw = Get-Content -Path $path -Raw -Encoding UTF8 -ErrorAction Stop
|
||||
return ConvertTo-DeepHashtable ($raw | ConvertFrom-Json)
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to read state.json, returning empty state: $_" -Feature 'Config'
|
||||
return @{}
|
||||
}
|
||||
}
|
||||
|
||||
function Save-State {
|
||||
param([hashtable]$State)
|
||||
|
||||
$path = Get-StatePath
|
||||
$dir = Split-Path $path -Parent
|
||||
if (-not (Test-Path $dir)) {
|
||||
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
||||
}
|
||||
try {
|
||||
$State | ConvertTo-Json -Depth 10 | Set-Content -Path $path -Encoding UTF8 -ErrorAction Stop
|
||||
}
|
||||
catch {
|
||||
Write-Log -Level Error -Message "Failed to save state.json: $_" -Feature 'Config'
|
||||
}
|
||||
}
|
||||
|
||||
# ── Feature config seeding ────────────────────────────────────────────────────
|
||||
|
||||
function Ensure-FeatureConfig {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Ensures config.json contains an entry for the given feature, with all
|
||||
settings keys present. Missing keys are filled from $FeatureMeta.Settings[*].Default.
|
||||
Returns the (potentially modified) config hashtable.
|
||||
#>
|
||||
param(
|
||||
[hashtable]$Config,
|
||||
[hashtable]$FeatureMeta
|
||||
)
|
||||
|
||||
$name = $FeatureMeta.Name
|
||||
|
||||
if (-not $Config.features.ContainsKey($name)) {
|
||||
$Config.features[$name] = @{ enabled = $false }
|
||||
}
|
||||
|
||||
foreach ($setting in $FeatureMeta.Settings) {
|
||||
if (-not $Config.features[$name].ContainsKey($setting.Key)) {
|
||||
$Config.features[$name][$setting.Key] = $setting.Default
|
||||
}
|
||||
}
|
||||
|
||||
return $Config
|
||||
}
|
||||
|
||||
# ── Internal helper ───────────────────────────────────────────────────────────
|
||||
|
||||
function ConvertTo-DeepHashtable {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Recursively converts PSCustomObject (from ConvertFrom-Json) into hashtables
|
||||
so all config/state values are mutable and support .ContainsKey().
|
||||
#>
|
||||
param([object]$InputObject)
|
||||
|
||||
if ($null -eq $InputObject) { return $null }
|
||||
if ($InputObject -is [hashtable]) { return $InputObject }
|
||||
if ($InputObject -is [System.Collections.IEnumerable] -and
|
||||
$InputObject -isnot [string]) {
|
||||
return @($InputObject | ForEach-Object { ConvertTo-DeepHashtable $_ })
|
||||
}
|
||||
if ($InputObject -is [PSCustomObject]) {
|
||||
$hash = @{}
|
||||
foreach ($prop in $InputObject.PSObject.Properties) {
|
||||
$hash[$prop.Name] = ConvertTo-DeepHashtable $prop.Value
|
||||
}
|
||||
return $hash
|
||||
}
|
||||
return $InputObject
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user