Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Read our contribution guide in our organization level

## Recommended Tools

| Tool | Description |
| Tool | Description |
|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------|
| <a href="https://www.jetbrains.com/clion/"><img src="https://resources.jetbrains.com/storage/products/company/brand/logos/CLion_icon.svg" width="30" height="30"></a><br>CLion | Recommended IDE for C and C++ development. Free for non-commercial use. |

Expand All @@ -16,6 +16,7 @@ Read our contribution guide in our organization level
* [EJS](https://www.npmjs.com/package/vite-plugin-ejs) is used as a templating system for the pages
(check `template_header.html` and `template_header_main.html`).
* The Style System is provided by [Bootstrap](https://getbootstrap.com).
* Icons are provided by [Lucide](https://lucide.dev) and [Simple Icons](https://simpleicons.org).
* The JS framework used by the more interactive pages is [Vus.js](https://vuejs.org).

#### Building
Expand Down
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@
"type": "module",
"dependencies": {
"@lizardbyte/shared-web": "2025.922.181114",
"date-fns": "4.1.0",
"lucide-vue-next": "0.563.0",
"marked": "17.0.1",
"vue": "3.5.27",
"vue-i18n": "11.2.8"
"vue-i18n": "11.2.8",
"vue3-simple-icons": "15.6.0"
},
"devDependencies": {
"@codecov/vite-plugin": "1.9.1",
Expand Down
31 changes: 28 additions & 3 deletions src/confighttp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,26 @@ namespace confighttp {
response->write(content, headers);
}

/**
* @brief Get the featured apps page.
* @param response The HTTP response object.
* @param request The HTTP request object.
*/
void getFeaturedPage(resp_https_t response, req_https_t request) {
if (!authenticate(response, request)) {
return;
}

print_req(request);

std::string content = file_handler::read_file(WEB_DIR "featured.html");
SimpleWeb::CaseInsensitiveMultimap headers;
headers.emplace("Content-Type", "text/html; charset=utf-8");
headers.emplace("X-Frame-Options", "DENY");
headers.emplace("Content-Security-Policy", "frame-ancestors 'none';");
response->write(content, headers);
}

/**
* @brief Get the password page.
* @param response The HTTP response object.
Expand Down Expand Up @@ -955,9 +975,6 @@ namespace confighttp {
* @api_examples{/api/covers/9999 | GET| null}
*/
void getCover(resp_https_t response, req_https_t request) {
if (!check_content_type(response, request, "application/json")) {
return;
}
if (!authenticate(response, request)) {
return;
}
Expand Down Expand Up @@ -986,6 +1003,13 @@ namespace confighttp {
// This handles extension validation, PNG signature validation, and path resolution
std::string validated_path = proc::validate_app_image_path(app_image_path);

// Check if we got the default image path (means validation failed or no image configured)
if (validated_path == DEFAULT_APP_IMAGE_PATH) {
BOOST_LOG(debug) << "Application at index " << index << " does not have a valid cover image";
not_found(response, request, "Cover image not found");
return;
}

// Open and stream the validated file
std::ifstream in(validated_path, std::ios::binary);
if (!in) {
Expand Down Expand Up @@ -1410,6 +1434,7 @@ namespace confighttp {
server.resource["^/apps/?$"]["GET"] = getAppsPage;
server.resource["^/clients/?$"]["GET"] = getClientsPage;
server.resource["^/config/?$"]["GET"] = getConfigPage;
server.resource["^/featured/?$"]["GET"] = getFeaturedPage;
server.resource["^/password/?$"]["GET"] = getPasswordPage;
server.resource["^/welcome/?$"]["GET"] = getWelcomePage;
server.resource["^/troubleshooting/?$"]["GET"] = getTroubleshootingPage;
Expand Down
82 changes: 45 additions & 37 deletions src_assets/common/assets/web/Navbar.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<nav class="navbar navbar-light navbar-expand-lg navbar-background header">
<nav class="navbar navbar-expand-lg navbar-sunshine">
<div class="container-fluid">
<a class="navbar-brand" href="./" title="Sunshine">
<img src="/images/logo-sunshine-45.png" height="45" alt="Sunshine">
Expand All @@ -11,22 +11,46 @@
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link" href="./"><i class="fas fa-fw fa-home"></i> {{ $t('navbar.home') }}</a>
<a class="nav-link" href="./">
<Home :size="18" class="icon"></Home>
{{ $t('navbar.home') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./pin"><i class="fas fa-fw fa-unlock"></i> {{ $t('navbar.pin') }}</a>
<a class="nav-link" href="./pin">
<Lock :size="18" class="icon"></Lock>
{{ $t('navbar.pin') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./apps"><i class="fas fa-fw fa-stream"></i> {{ $t('navbar.applications') }}</a>
<a class="nav-link" href="./apps">
<Layers :size="18" class="icon"></Layers>
{{ $t('navbar.applications') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./config"><i class="fas fa-fw fa-cog"></i> {{ $t('navbar.configuration') }}</a>
<a class="nav-link" href="./featured">
<Star :size="18" class="icon"></Star>
{{ $t('navbar.featured') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./password"><i class="fas fa-fw fa-user-shield"></i> {{ $t('navbar.password') }}</a>
<a class="nav-link" href="./config">
<Settings :size="18" class="icon"></Settings>
{{ $t('navbar.configuration') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./troubleshooting"><i class="fas fa-fw fa-info"></i> {{ $t('navbar.troubleshoot') }}</a>
<a class="nav-link" href="./password">
<Shield :size="18" class="icon"></Shield>
{{ $t('navbar.password') }}
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="./troubleshooting">
<Info :size="18" class="icon"></Info>
{{ $t('navbar.troubleshoot') }}
</a>
</li>
<li class="nav-item">
<ThemeToggle/>
Expand All @@ -38,10 +62,20 @@
</template>

<script>
import { Home, Lock, Layers, Star, Settings, Shield, Info } from 'lucide-vue-next'
import ThemeToggle from './ThemeToggle.vue'

export default {
components: { ThemeToggle },
components: {
ThemeToggle,
Home,
Lock,
Layers,
Star,
Settings,
Shield,
Info
},
created() {
console.log("Header mounted!")
},
Expand All @@ -53,34 +87,8 @@ export default {
</script>

<style>
.navbar-background {
background-color: #ffc400
}

.header .nav-link {
color: rgba(0, 0, 0, .65) !important;
}

.header .nav-link.active {
color: rgb(0, 0, 0) !important;
font-weight: 500;
}

.header .nav-link:hover {
color: rgb(0, 0, 0) !important;
font-weight: 500;
}

.header .navbar-toggler {
color: rgba(var(--bs-dark-rgb), .65) !important;
border: var(--bs-border-width) solid rgba(var(--bs-dark-rgb), 0.15) !important;
}

.header .navbar-toggler-icon {
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
}

.form-control::placeholder {
opacity: 0.5;
/* Navbar toggler icon for dark text on light background */
.navbar-sunshine .navbar-toggler-icon {
--bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.9%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") !important;
}
</style>
58 changes: 42 additions & 16 deletions src_assets/common/assets/web/ResourceCard.vue
Original file line number Diff line number Diff line change
@@ -1,33 +1,59 @@
<template>
<div class="card p-2">
<div class="card">
<div class="card-body">
<h2>{{ $t('resource_card.resources') }}</h2>
<br>
<p>{{ $t('resource_card.resources_desc') }}</p>
<div class="card-group p-4 align-items-center">
<a class="btn btn-success m-1" href="https://app.lizardbyte.dev" target="_blank">
{{ $t('resource_card.lizardbyte_website') }}</a>
<a class="btn btn-primary m-1" href="https://app.lizardbyte.dev/discord" target="_blank">
<i class="fab fa-fw fa-discord"></i> Discord</a>
<a class="btn btn-secondary m-1" href="https://github.com/orgs/LizardByte/discussions" target="_blank">
<i class="fab fa-fw fa-github"></i> {{ $t('resource_card.github_discussions') }}</a>
<div class="d-flex flex-wrap gap-2 mt-4">
<a class="btn btn-success" href="https://app.lizardbyte.dev" target="_blank">
<Globe :size="18" class="icon"></Globe>
{{ $t('resource_card.lizardbyte_website') }}
</a>
<a class="btn btn-primary" href="https://app.lizardbyte.dev/discord" target="_blank">
<SimpleIcon icon="Discord" :size="18" class="icon"></SimpleIcon>
Discord
</a>
<a class="btn btn-secondary" href="https://github.com/orgs/LizardByte/discussions" target="_blank">
<SimpleIcon icon="GitHub" :size="18" class="icon"></SimpleIcon>
{{ $t('resource_card.github_discussions') }}
</a>
</div>
</div>
</div>
<!-- Legal -->
<div class="card p-2 mt-4">
<div class="card mt-4">
<div class="card-body">
<h2>{{ $t('resource_card.legal') }}</h2>
<br>
<p>{{ $t('resource_card.legal_desc') }}</p>
<div class="card-group p-4 align-items-center">
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
<div class="d-flex flex-wrap gap-2 mt-4">
<a class="btn btn-danger" href="https://github.com/LizardByte/Sunshine/blob/master/LICENSE"
target="_blank">
<i class="fas fa-fw fa-file-alt"></i> {{ $t('resource_card.license') }}</a>
<a class="btn btn-danger m-1" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
<FileText :size="18" class="icon"></FileText>
{{ $t('resource_card.license') }}
</a>
<a class="btn btn-danger" href="https://github.com/LizardByte/Sunshine/blob/master/NOTICE"
target="_blank">
<i class="fas fa-fw fa-exclamation"></i> {{ $t('resource_card.third_party_notice') }}</a>
<AlertCircle :size="18" class="icon"></AlertCircle>
{{ $t('resource_card.third_party_notice') }}
</a>
</div>
</div>
</div>
</template>

<script>
import {
AlertCircle,
FileText,
Globe,
} from 'lucide-vue-next'
import SimpleIcon from './SimpleIcon.vue'

export default {
components: {
SimpleIcon,
AlertCircle,
FileText,
Globe,
}
}
</script>
44 changes: 44 additions & 0 deletions src_assets/common/assets/web/SimpleIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<template>
<component
v-if="iconComponent"
:is="iconComponent"
:size="size"
:class="className"
/>
</template>

<script setup>
import { computed } from 'vue'
import { GitHubIcon, DiscordIcon } from 'vue3-simple-icons'

const props = defineProps({
icon: {
type: String,
required: true,
default: 'GitHub'
},
size: {
type: [Number, String],
default: 24
},
className: {
type: String,
default: ''
}
})

// Map icon names to actual components
const iconMap = {
'GitHub': GitHubIcon,
'Discord': DiscordIcon,
}

const iconComponent = computed(() => {
const component = iconMap[props.icon]
if (!component) {
console.error(`Icon "${props.icon}" not found in SimpleIcon mapping`)
return null
}
return component
})
</script>
Loading
Loading