mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-07-17 14:33:27 +02:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
7e58423269 | |||
2657aae847 | |||
13cb85ac5a | |||
1a973e09a0 | |||
f799927f90 | |||
8695f0026f | |||
3b552dba8a | |||
1ce60d8291 | |||
b5097b5a03 | |||
610842abac | |||
35c71aba64 | |||
d79d949d65 | |||
b3f94b0d90 | |||
96207f3960 | |||
cd7a7fbe05 |
684
Cargo.lock
generated
684
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -7,14 +7,14 @@ authors = ["Slatian <baschdel@disroot.org>"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
lib-humus = { version="0.2", features=["axum-view+cookie"], git="https://codeberg.org/slatian/lib-humus.git" }
|
lib-humus = { version="0.2", features=["axum-view+cookie"] }
|
||||||
|
|
||||||
axum = { version = "0.7", features = ["macros"] }
|
axum = { version = "0.7", features = ["macros"] }
|
||||||
axum-extra = { version = "0.9", features = ["cookie", "typed-header"] }
|
axum-extra = { version = "0.9", features = ["cookie", "typed-header"] }
|
||||||
axum-client-ip = "0.5"
|
axum-client-ip = "0.6"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
governor = "0.6"
|
governor = "0.6"
|
||||||
idna = "0.4"
|
idna = "0.5"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
parking_lot = "0.12"
|
parking_lot = "0.12"
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
@ -26,6 +26,6 @@ tower = "0.4"
|
|||||||
tower-http = { version = "0.5", features = ["fs"] }
|
tower-http = { version = "0.5", features = ["fs"] }
|
||||||
hickory-proto = "0.24"
|
hickory-proto = "0.24"
|
||||||
hickory-resolver = { version = "0.24", features = ["dns-over-rustls","dns-over-https","dns-over-quic","native-certs"] }
|
hickory-resolver = { version = "0.24", features = ["dns-over-rustls","dns-over-https","dns-over-quic","native-certs"] }
|
||||||
maxminddb = "0.23"
|
maxminddb = "0.24"
|
||||||
mime = "0.3"
|
mime = "0.3"
|
||||||
http = "1.0"
|
http = "1.1"
|
||||||
|
@ -86,7 +86,7 @@ impl Into<Protocol> for DnsProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DnsResolverConfig {
|
impl DnsResolverConfig {
|
||||||
pub fn to_trust_resolver_config(
|
pub fn to_hickory_resolver_config(
|
||||||
&self
|
&self
|
||||||
) -> HickoryResolverConfig {
|
) -> HickoryResolverConfig {
|
||||||
let mut resolver = HickoryResolverConfig::new();
|
let mut resolver = HickoryResolverConfig::new();
|
||||||
|
@ -6,7 +6,7 @@ use std::num::NonZeroU32;
|
|||||||
|
|
||||||
mod dns;
|
mod dns;
|
||||||
|
|
||||||
pub use crate::config::dns::{DnsConfig, DnsProtocol, DnsResolverConfig};
|
pub use crate::config::dns::{DnsConfig, DnsResolverConfig};
|
||||||
|
|
||||||
#[derive(Deserialize, Default, Clone)]
|
#[derive(Deserialize, Default, Clone)]
|
||||||
pub struct EchoIpServiceConfig {
|
pub struct EchoIpServiceConfig {
|
||||||
|
44
src/main.rs
44
src/main.rs
@ -67,6 +67,7 @@ pub struct SettingsQuery {
|
|||||||
format: Option<ResponseFormat>,
|
format: Option<ResponseFormat>,
|
||||||
lang: Option<String>,
|
lang: Option<String>,
|
||||||
dns: Option<String>,
|
dns: Option<String>,
|
||||||
|
dns_self_lookup: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
@ -82,6 +83,7 @@ pub struct IpResult {
|
|||||||
location: Option<LocationResult>,
|
location: Option<LocationResult>,
|
||||||
ip_info: AddressInfo,
|
ip_info: AddressInfo,
|
||||||
used_dns_resolver: Option<Arc<str>>,
|
used_dns_resolver: Option<Arc<str>>,
|
||||||
|
reverse_dns_disabled_for_privacy: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need this one to hide the partial lookup field when irelevant
|
// We need this one to hide the partial lookup field when irelevant
|
||||||
@ -227,7 +229,7 @@ async fn main() {
|
|||||||
for (key, resolver_config) in &config.dns.resolver {
|
for (key, resolver_config) in &config.dns.resolver {
|
||||||
println!("Initalizing {} resolver ...", key);
|
println!("Initalizing {} resolver ...", key);
|
||||||
let resolver = TokioAsyncResolver::tokio(
|
let resolver = TokioAsyncResolver::tokio(
|
||||||
resolver_config.to_trust_resolver_config(),
|
resolver_config.to_hickory_resolver_config(),
|
||||||
Default::default()
|
Default::default()
|
||||||
);
|
);
|
||||||
dns_resolver_map.insert(key.clone(), resolver);
|
dns_resolver_map.insert(key.clone(), resolver);
|
||||||
@ -357,6 +359,7 @@ async fn settings_query_middleware(
|
|||||||
lang: query.lang.unwrap_or("en".to_string()),
|
lang: query.lang.unwrap_or("en".to_string()),
|
||||||
available_dns_resolvers: derived_config.dns_resolver_selectables,
|
available_dns_resolvers: derived_config.dns_resolver_selectables,
|
||||||
dns_resolver_id: dns_resolver_id,
|
dns_resolver_id: dns_resolver_id,
|
||||||
|
dns_disable_self_lookup: !query.dns_self_lookup.unwrap_or(false),
|
||||||
});
|
});
|
||||||
next.run(req).await
|
next.run(req).await
|
||||||
}
|
}
|
||||||
@ -400,7 +403,7 @@ async fn handle_default_route(
|
|||||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
Extension(settings): Extension<QuerySettings>,
|
Extension(settings): Extension<QuerySettings>,
|
||||||
user_agent_header: Option<TypedHeader<headers::UserAgent>>,
|
user_agent_header: Option<TypedHeader<headers::UserAgent>>,
|
||||||
SecureClientIp(address): SecureClientIp
|
SecureClientIp(client_ip): SecureClientIp
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
|
||||||
let state = Arc::clone(&arc_state);
|
let state = Arc::clone(&arc_state);
|
||||||
@ -411,12 +414,20 @@ async fn handle_default_route(
|
|||||||
search_query,
|
search_query,
|
||||||
false,
|
false,
|
||||||
settings,
|
settings,
|
||||||
state
|
state,
|
||||||
|
&client_ip
|
||||||
).await;
|
).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = get_ip_result(&address, &settings.lang, &"default".into(), &state).await;
|
let result = get_ip_result(
|
||||||
|
&client_ip,
|
||||||
|
&settings.lang,
|
||||||
|
&settings.dns_resolver_id,
|
||||||
|
settings.dns_disable_self_lookup,
|
||||||
|
&client_ip,
|
||||||
|
&state,
|
||||||
|
).await;
|
||||||
|
|
||||||
let user_agent: Option<String> = match user_agent_header {
|
let user_agent: Option<String> = match user_agent_header {
|
||||||
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
|
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
|
||||||
@ -438,6 +449,7 @@ async fn handle_search_request(
|
|||||||
this_should_have_been_an_ip: bool,
|
this_should_have_been_an_ip: bool,
|
||||||
settings: QuerySettings,
|
settings: QuerySettings,
|
||||||
arc_state: Arc<ServiceSharedState>,
|
arc_state: Arc<ServiceSharedState>,
|
||||||
|
client_ip: &IpAddr,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
|
||||||
let mut search_query = search_query.trim().to_string();
|
let mut search_query = search_query.trim().to_string();
|
||||||
@ -474,7 +486,7 @@ async fn handle_search_request(
|
|||||||
|
|
||||||
// Try to interpret as an IP-Address
|
// Try to interpret as an IP-Address
|
||||||
if let Ok(address) = search_query.parse() {
|
if let Ok(address) = search_query.parse() {
|
||||||
return handle_ip_request(address, settings, arc_state).await;
|
return handle_ip_request(address, settings, arc_state, client_ip).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to treating it as a hostname
|
// Fall back to treating it as a hostname
|
||||||
@ -522,11 +534,12 @@ async fn handle_ip_route_with_path(
|
|||||||
Extension(settings): Extension<QuerySettings>,
|
Extension(settings): Extension<QuerySettings>,
|
||||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
extract::Path(query): extract::Path<String>,
|
extract::Path(query): extract::Path<String>,
|
||||||
|
SecureClientIp(client_ip): SecureClientIp
|
||||||
) -> Response {
|
) -> Response {
|
||||||
if let Ok(address) = query.parse() {
|
if let Ok(address) = query.parse() {
|
||||||
return handle_ip_request(address, settings, arc_state).await
|
return handle_ip_request(address, settings, arc_state, &client_ip).await
|
||||||
} else {
|
} else {
|
||||||
return handle_search_request(query, true, settings, arc_state).await;
|
return handle_search_request(query, true, settings, arc_state, &client_ip).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -534,6 +547,7 @@ async fn handle_ip_request(
|
|||||||
address: IpAddr,
|
address: IpAddr,
|
||||||
settings: QuerySettings,
|
settings: QuerySettings,
|
||||||
arc_state: Arc<ServiceSharedState>,
|
arc_state: Arc<ServiceSharedState>,
|
||||||
|
client_ip: &IpAddr,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
|
||||||
let state = Arc::clone(&arc_state);
|
let state = Arc::clone(&arc_state);
|
||||||
@ -541,6 +555,8 @@ async fn handle_ip_request(
|
|||||||
&address,
|
&address,
|
||||||
&settings.lang,
|
&settings.lang,
|
||||||
&settings.dns_resolver_id,
|
&settings.dns_resolver_id,
|
||||||
|
settings.dns_disable_self_lookup,
|
||||||
|
client_ip,
|
||||||
&state).await;
|
&state).await;
|
||||||
|
|
||||||
state.templating_engine.render_view(
|
state.templating_engine.render_view(
|
||||||
@ -553,9 +569,19 @@ async fn get_ip_result(
|
|||||||
address: &IpAddr,
|
address: &IpAddr,
|
||||||
lang: &String,
|
lang: &String,
|
||||||
dns_resolver_name: &Arc<str>,
|
dns_resolver_name: &Arc<str>,
|
||||||
|
dns_disable_self_lookup: bool,
|
||||||
|
client_ip: &IpAddr,
|
||||||
state: &ServiceSharedState,
|
state: &ServiceSharedState,
|
||||||
) -> IpResult {
|
) -> IpResult {
|
||||||
|
|
||||||
|
let mut reverse_dns_disabled_for_privacy = false;
|
||||||
|
|
||||||
|
if state.config.dns.allow_reverse_lookup {
|
||||||
|
if address == client_ip && dns_disable_self_lookup {
|
||||||
|
reverse_dns_disabled_for_privacy = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let ip_info = AddressInfo::new(&address);
|
let ip_info = AddressInfo::new(&address);
|
||||||
|
|
||||||
if !(ip_info.scope == AddressScope::Global || ip_info.scope == AddressScope::Shared) || ip_info.cast != AddressCast::Unicast {
|
if !(ip_info.scope == AddressScope::Global || ip_info.scope == AddressScope::Shared) || ip_info.cast != AddressCast::Unicast {
|
||||||
@ -567,6 +593,7 @@ async fn get_ip_result(
|
|||||||
location: None,
|
location: None,
|
||||||
ip_info: ip_info,
|
ip_info: ip_info,
|
||||||
used_dns_resolver: None,
|
used_dns_resolver: None,
|
||||||
|
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -574,7 +601,7 @@ async fn get_ip_result(
|
|||||||
// do reverse lookup
|
// do reverse lookup
|
||||||
let mut hostname: Option<String> = None;
|
let mut hostname: Option<String> = None;
|
||||||
let mut used_dns_resolver: Option<Arc<str>> = None;
|
let mut used_dns_resolver: Option<Arc<str>> = None;
|
||||||
if state.config.dns.allow_reverse_lookup {
|
if state.config.dns.allow_reverse_lookup && !reverse_dns_disabled_for_privacy {
|
||||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||||
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
|
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
|
||||||
used_dns_resolver = Some(dns_resolver_name.clone());
|
used_dns_resolver = Some(dns_resolver_name.clone());
|
||||||
@ -605,6 +632,7 @@ async fn get_ip_result(
|
|||||||
location: location_result,
|
location: location_result,
|
||||||
ip_info: ip_info,
|
ip_info: ip_info,
|
||||||
used_dns_resolver: used_dns_resolver,
|
used_dns_resolver: used_dns_resolver,
|
||||||
|
reverse_dns_disabled_for_privacy: reverse_dns_disabled_for_privacy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ pub struct QuerySettings {
|
|||||||
pub lang: String,
|
pub lang: String,
|
||||||
pub available_dns_resolvers: Vec<Selectable>,
|
pub available_dns_resolvers: Vec<Selectable>,
|
||||||
pub dns_resolver_id: Arc<str>,
|
pub dns_resolver_id: Arc<str>,
|
||||||
|
pub dns_disable_self_lookup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Clone)]
|
#[derive(Deserialize, Serialize, Clone)]
|
||||||
|
@ -24,7 +24,11 @@
|
|||||||
<body>
|
<body>
|
||||||
<header>
|
<header>
|
||||||
<nav>
|
<nav>
|
||||||
<a href="{{ extra.base_url }}" class="sitename">{{extra.site_name|default(value="echoip")}}</a>
|
<a href="{{ extra.base_url }}" class="sitename">
|
||||||
|
{%- if extra.display_icon -%}
|
||||||
|
<img src="{{extra.display_icon}}" alt="">
|
||||||
|
{%- endif -%}
|
||||||
|
{{extra.site_name|default(value="echoip")}}</a>
|
||||||
<form class="search" method="GET" action="{{ extra.base_url }}">
|
<form class="search" method="GET" action="{{ extra.base_url }}">
|
||||||
<input type="search" name="query" autocomplete="on" maxlength="260"
|
<input type="search" name="query" autocomplete="on" maxlength="260"
|
||||||
title="Search for an IP-Adress, Domain-Name, or ASN."
|
title="Search for an IP-Adress, Domain-Name, or ASN."
|
||||||
|
@ -8,10 +8,14 @@ base_url="http://localhost:3000"
|
|||||||
stylesheet = "/style.css"
|
stylesheet = "/style.css"
|
||||||
|
|
||||||
# URL to and mimetype of your favicon
|
# URL to and mimetype of your favicon
|
||||||
# favicon = ""
|
favicon = "/icon_64.png"
|
||||||
# favicon_mimetype = "image/png"
|
favicon_mimetype = "image/png"
|
||||||
|
# favicon_mimetype = "image/svg+xml"
|
||||||
# favicon_mimetype = "image/jpeg"
|
# favicon_mimetype = "image/jpeg"
|
||||||
|
|
||||||
|
# Icon to display next to the title
|
||||||
|
display_icon = "/icon_64.png"
|
||||||
|
|
||||||
# URLs to look up v4 and v6 addresses explicitly
|
# URLs to look up v4 and v6 addresses explicitly
|
||||||
# If you have not configured them, comment them out, the button will stay hidden
|
# If you have not configured them, comment them out, the button will stay hidden
|
||||||
v4_url="http://v4.localhost:3000/"
|
v4_url="http://v4.localhost:3000/"
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
<a href="{{ self::dig_link(extra=extra, name=name) }}">{% if prefix %}{{ prefix }} {% endif %}{% if fqdn or name=="." %}{{ name }}{% else %}{{ name | trim_end_matches(pat=".") }}{% endif %}</a>
|
<a href="{{ self::dig_link(extra=extra, name=name) }}">{% if prefix %}{{ prefix }} {% endif %}{% if fqdn or name=="." %}{{ name }}{% else %}{{ name | trim_end_matches(pat=".") }}{% endif %}</a>
|
||||||
{% endmacro dig %}
|
{% endmacro dig %}
|
||||||
|
|
||||||
{% macro ip(extra, ip, text=false) %}
|
{% macro ip(extra, ip, text=false, with_self_lookup=false) %}
|
||||||
<a href="{{ extra.base_url }}/ip/{{ ip | urlencode_strict | replace(from="%2e", to=".") | replace(from="%3a", to=":") | safe }}"><code>{% if text %}{{ text }}{% else %}{{ ip }}{% endif %}</code></a>
|
<a href="{{ extra.base_url }}/ip/{{ ip | urlencode_strict | replace(from="%2e", to=".") | replace(from="%3a", to=":") | safe }}{% if with_self_lookup %}?dns_self_lookup=true{% endif %}"><code>{% if text %}{{ text }}{% else %}{{ ip }}{% endif %}</code></a>
|
||||||
{% endmacro dig %}
|
{% endmacro dig %}
|
||||||
|
|
||||||
{% macro breadcrumb_domain(extra, name) %}
|
{% macro breadcrumb_domain(extra, name) %}
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
{% if r.hostname %}
|
{% if r.hostname %}
|
||||||
<dt>Hostname</dt>
|
<dt>Hostname</dt>
|
||||||
<dd>{{ helper::dig(extra=extra, name=r.hostname) }}</dd>
|
<dd>{{ helper::dig(extra=extra, name=r.hostname) }}</dd>
|
||||||
|
{% elif r.reverse_dns_disabled_for_privacy %}
|
||||||
|
<dt>Hostname</dt>
|
||||||
|
<dd>Lookup disabled by default: {{ helper::ip(ip=r.address, extra=extra, text="enable", with_self_lookup=true)}}</dd>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if r.asn %}
|
{% if r.asn %}
|
||||||
<dt><abbr="Autonomous System Number">ASN</abbr></dt>
|
<dt><abbr="Autonomous System Number">ASN</abbr></dt>
|
||||||
|
@ -13,12 +13,19 @@
|
|||||||
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
|
* Type of Address: {{ helper::ip_info(ip_info=r.ip_info) }}
|
||||||
{% if r.hostname -%}
|
{% if r.hostname -%}
|
||||||
* Hostname: {{ r.hostname }}
|
* Hostname: {{ r.hostname }}
|
||||||
|
{%- elif r.reverse_dns_disabled_for_privacy %}
|
||||||
|
* Hostname: Lookup disabled by default
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
{% if r.asn -%}
|
{% if r.asn -%}
|
||||||
* ASN: AS{{ r.asn.asn }}
|
* ASN: AS{{ r.asn.asn }}
|
||||||
* AS Name: {{r.asn.name}}
|
* AS Name: {{r.asn.name}}
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
|
{%- if r.reverse_dns_disabled_for_privacy %}
|
||||||
|
|
||||||
|
=> /ip/{{ data.result.address }}?dns_self_lookup=true Do a reverse DNS lookup
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{%- if r.location %}
|
{%- if r.location %}
|
||||||
|
|
||||||
## Geolocation
|
## Geolocation
|
||||||
|
50
templates/static/icon.svg
Normal file
50
templates/static/icon.svg
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
width="48"
|
||||||
|
height="48"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg">
|
||||||
|
<defs
|
||||||
|
id="defs1">
|
||||||
|
<linearGradient
|
||||||
|
id="linearGradient8">
|
||||||
|
<stop
|
||||||
|
style="stop-color:#fb9a00;stop-opacity:1;"
|
||||||
|
offset="0"
|
||||||
|
id="stop8" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#884f00;stop-opacity:1;"
|
||||||
|
offset="0.49966338"
|
||||||
|
id="stop10" />
|
||||||
|
<stop
|
||||||
|
style="stop-color:#be8700;stop-opacity:1;"
|
||||||
|
offset="1"
|
||||||
|
id="stop9" />
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient
|
||||||
|
xlink:href="#linearGradient8"
|
||||||
|
id="linearGradient9"
|
||||||
|
x1="10.202637"
|
||||||
|
y1="35.241699"
|
||||||
|
x2="39.21582"
|
||||||
|
y2="12.833984"
|
||||||
|
gradientUnits="userSpaceOnUse" />
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
id="layer1">
|
||||||
|
<path
|
||||||
|
id="path2"
|
||||||
|
style="fill:url(#linearGradient9);fill-opacity:1;stroke-width:3.15427;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||||
|
d="m 2,7 v 33.767595 l 1.586,0.0021 L 8.299716,45.41681 12.826,40.767584 H 46 V 7 Z" />
|
||||||
|
<path
|
||||||
|
id="rect1"
|
||||||
|
style="fill:#111111;stroke-width:3;stroke-linejoin:round;paint-order:stroke markers fill"
|
||||||
|
d="M 3 8 L 3 40 L 4.0019531 40 L 4 40.001953 L 8.2792969 44.205078 L 12.412109 40 L 45 40 L 45 8 L 3 8 z M 35.671875 11.712891 L 39.357422 11.712891 L 39.357422 36.287109 L 35.671875 36.287109 L 35.671875 17.033203 L 31.494141 21.363281 L 28.839844 18.804688 C 31.107109 16.462871 35.671875 11.712891 35.671875 11.712891 z M 8.6425781 21.542969 L 12.328125 21.542969 L 12.328125 25.228516 L 8.6425781 25.228516 L 8.6425781 21.542969 z M 20.927734 21.542969 L 24.615234 21.542969 L 24.615234 25.228516 L 20.927734 25.228516 L 20.927734 21.542969 z M 8.6425781 32.599609 L 12.328125 32.599609 L 12.328125 36.287109 L 8.6425781 36.287109 L 8.6425781 32.599609 z M 20.927734 32.599609 L 24.615234 32.599609 L 24.615234 36.287109 L 20.927734 36.287109 L 20.927734 32.599609 z " />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
BIN
templates/static/icon_128.png
Normal file
BIN
templates/static/icon_128.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
templates/static/icon_32.png
Normal file
BIN
templates/static/icon_32.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 950 B |
BIN
templates/static/icon_64.png
Normal file
BIN
templates/static/icon_64.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
@ -599,3 +599,10 @@ form.search {
|
|||||||
background: var(--button-bg);
|
background: var(--button-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Custom icon style for sitename*/
|
||||||
|
|
||||||
|
.sitename > img {
|
||||||
|
height: 1.2em;
|
||||||
|
padding: 0 0.3ch;
|
||||||
|
margin-bottom: -.2em;
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user