Skip to content

Commit 146427b

Browse files
feat: support XDG Base Directory Specification
Signed-off-by: Henry Gressmann <[email protected]>
1 parent 7df6c89 commit 146427b

27 files changed

+141
-123
lines changed

Dockerfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ RUN [ "${TARGETPLATFORM}" = "linux/arm64" ] && export TAR_URL=${TAR_URL_ARM64} |
1010
&& chmod +x /app/liwan
1111

1212
FROM scratch
13+
14+
ENV LIWAN_CONFIG=/app/liwan.config.toml
15+
ENV LIWAN_DATA_DIR=/data
16+
1317
COPY --from=downloader /app/liwan /liwan
1418
ENTRYPOINT ["/liwan"]
15-
EXPOSE 8080
19+
EXPOSE 9042

config.example.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# The base URL of the Liwan instance
2-
base_url="http://localhost:8080"
2+
base_url="http://localhost:9042"
33

44
# The port to listen on (http)
5-
port=8080
5+
port=9042
66

7-
# Folder to store the database in (Will be created if it doesn't exist)
8-
data_dir="./liwan-data"
7+
# # Folder to store the database in (Will be created if it doesn't exist)
8+
# # defaults to ./.local/share/liwan/data (or $XDG_DATA_HOME/liwan/data) on linux/macos
9+
# # defaults to ./liwan-data on other platforms
10+
# data_dir="./liwan-data"
911

1012
# GeoIp settings (Optional)
1113
[geoip]

src/cli.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ pub(crate) struct SeedDatabase {}
4545
#[derive(FromArgs)]
4646
#[argh(subcommand, name = "generate-config")]
4747
/// Save a default configuration file to `liwan.config.toml`
48-
pub(crate) struct GenConfig {}
48+
pub(crate) struct GenConfig {
49+
#[argh(option, short = 'o')]
50+
/// the path to write the configuration file to
51+
output: Option<String>,
52+
}
4953

5054
#[derive(FromArgs)]
5155
#[argh(subcommand, name = "update-password")]
@@ -114,13 +118,14 @@ pub(crate) fn handle_command(mut config: Config, cmd: Command) -> Result<()> {
114118

115119
println!("User {} created", add.username);
116120
}
117-
Command::GenerateConfig(_) => {
118-
if std::path::Path::new("liwan.config.toml").exists() {
121+
Command::GenerateConfig(GenConfig { output }) => {
122+
let output = output.unwrap_or_else(|| "liwan.config.toml".to_string());
123+
if std::path::Path::new(&output).exists() {
119124
println!("Configuration file already exists");
120125
return Ok(());
121126
}
122127

123-
std::fs::write("liwan.config.toml", DEFAULT_CONFIG)?;
128+
std::fs::write(&output, DEFAULT_CONFIG)?;
124129
println!("Configuration file written to liwan.config.toml");
125130
}
126131
#[cfg(debug_assertions)]

src/config.rs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,25 @@ use serde::{Deserialize, Serialize};
66
use std::str::FromStr;
77

88
fn default_base() -> String {
9-
"http://localhost:8080".to_string()
9+
"http://localhost:9042".to_string()
1010
}
1111

1212
fn default_port() -> u16 {
13-
8080
13+
9042
1414
}
1515

1616
fn default_data_dir() -> String {
17+
#[cfg(target_family = "unix")]
18+
{
19+
if std::path::Path::new("~/.local/share").exists() {
20+
return "~/.local/share/liwan/data".to_string();
21+
}
22+
std::env::var("XDG_DATA_HOME")
23+
.map(|home| format!("{}/liwan/data", home))
24+
.unwrap_or_else(|_| "./liwan-data".to_string())
25+
}
26+
27+
#[cfg(not(target_family = "unix"))]
1728
"./liwan-data".to_string()
1829
}
1930

@@ -45,7 +56,22 @@ impl Config {
4556
pub(crate) fn load(path: Option<String>) -> Result<Self> {
4657
tracing::debug!(path = ?path, "loading config");
4758

59+
let path = path.or_else(|| std::env::var("LIWAN_CONFIG").ok());
60+
61+
#[cfg(target_family = "unix")]
62+
let path = path.or_else(|| match std::env::var("XDG_CONFIG_HOME") {
63+
Ok(home) => Some(format!("{}/liwan/config.toml", home)),
64+
Err(_) => {
65+
if std::path::Path::new("~/.config").exists() {
66+
Some("~/.config/liwan/config.toml".to_string())
67+
} else {
68+
None
69+
}
70+
}
71+
});
72+
4873
let config: Config = Figment::new()
74+
.merge(Toml::file("liwan.config.toml".to_string()))
4975
.merge(Toml::file(path.unwrap_or("liwan.config.toml".to_string())))
5076
.merge(Env::raw().filter_map(|key| match key {
5177
k if !k.starts_with("LIWAN_") => None,
@@ -102,7 +128,7 @@ mod test {
102128
assert_eq!(config.geoip.as_ref().unwrap().maxmind_db_path, Some("test".to_string()));
103129
assert_eq!(config.base_url, "http://localhost:8081");
104130
assert_eq!(config.data_dir, "./liwan-test-data");
105-
assert_eq!(config.port, 8080);
131+
assert_eq!(config.port, 9042);
106132
Ok(())
107133
});
108134
}
@@ -123,7 +149,7 @@ mod test {
123149
assert!(config.geoip.is_none());
124150
assert_eq!(config.base_url, "http://localhost:8081");
125151
assert_eq!(config.data_dir, "./liwan-test-data");
126-
assert_eq!(config.port, 8080);
152+
assert_eq!(config.port, 9042);
127153
Ok(())
128154
});
129155
}
@@ -133,9 +159,8 @@ mod test {
133159
Jail::expect_with(|_jail| {
134160
let config = Config::load(None).expect("failed to load config");
135161
assert!(config.geoip.is_none());
136-
assert_eq!(config.base_url, "http://localhost:8080");
137-
assert_eq!(config.data_dir, "./liwan-data");
138-
assert_eq!(config.port, 8080);
162+
assert_eq!(config.base_url, "http://localhost:9042");
163+
assert_eq!(config.port, 9042);
139164
Ok(())
140165
});
141166
}

web/astro.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const dirname = path.dirname(new URL(import.meta.url).pathname);
66

77
const proxy = {
88
"/api": {
9-
target: "http://localhost:8080",
9+
target: "http://localhost:9042",
1010
changeOrigin: true,
1111
cookieDomainRewrite: "localhost:4321",
1212
},

web/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"scripts": {
66
"dev": "bun run --bun astro dev",
77
"build": "bun run --bun astro build",
8-
"preview": "bun run --bun astro preview"
8+
"preview": "bun run --bun astro preview",
9+
"typecheck": "bun run --bun tsc --noEmit"
910
},
1011
"dependencies": {
1112
"@astrojs/react": "^3.6.2",

web/src/api/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { createClient, type NormalizeOAS, type OASModel } from "fets";
1+
import { type NormalizeOAS, type OASModel, createClient } from "fets";
22
export { queryClient, useMutation, useQuery, getUsername } from "./utils";
3+
import { useMemo } from "react";
34
import type dashboardspec from "./dashboard";
45
import { queryClient, useQuery } from "./utils";
5-
import { useMemo } from "react";
66

77
export type DashboardSpec = NormalizeOAS<typeof dashboardspec>;
88
export type Metric = OASModel<DashboardSpec, "Metric">;

web/src/api/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import {
2-
QueryClient,
3-
useQuery as _useQuery,
4-
useMutation as _useMutation,
52
type DefaultError,
3+
QueryClient,
64
type QueryKey,
75
type UseQueryOptions,
86
type UseQueryResult,
7+
useMutation as _useMutation,
8+
useQuery as _useQuery,
99
} from "@tanstack/react-query";
1010

1111
// get the username cookie or undefined if not set

web/src/components/dialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import styles from "./dialog.module.css";
21
import * as Dia from "@radix-ui/react-dialog";
2+
import styles from "./dialog.module.css";
33

44
export const Dialog = ({
55
title,

web/src/components/dimensions/index.tsx

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
import styles from "./dimensions.module.css";
2-
import { LinkIcon } from "lucide-react";
31
import * as Tabs from "@radix-ui/react-tabs";
2+
import { LinkIcon } from "lucide-react";
3+
import styles from "./dimensions.module.css";
44

55
import {
6-
dimensionNames,
7-
formatMetricVal,
8-
metricNames,
9-
useDimension,
106
type DateRange,
117
type Dimension,
128
type DimensionTableRow,
139
type Metric,
1410
type ProjectResponse,
11+
dimensionNames,
12+
formatMetricVal,
13+
metricNames,
14+
useDimension,
1515
} from "../../api";
1616

1717
import { BrowserIcon, MobileDeviceIcon, OSIcon, ReferrerIcon } from "../icons";
@@ -86,25 +86,9 @@ export const DimensionTable = ({
8686
range,
8787
}: { project: ProjectResponse; dimension: Dimension; metric: Metric; range: DateRange; noHeader?: boolean }) => {
8888
const { data, biggest, order } = useDimension({ project, dimension, metric, range });
89-
return <DimensionList value={data ?? []} dimension={dimension} metric={metric} biggest={biggest} order={order} />;
90-
};
91-
92-
export const DimensionList = ({
93-
value,
94-
dimension,
95-
metric,
96-
biggest,
97-
order,
98-
}: {
99-
value: DimensionTableRow[];
100-
dimension: Dimension;
101-
metric: Metric;
102-
biggest: number;
103-
order?: string[];
104-
}) => {
10589
return (
10690
<div>
107-
{value.map((d) => {
91+
{data?.map((d) => {
10892
return (
10993
<div
11094
key={d.dimensionValue}

0 commit comments

Comments
 (0)