Compare commits

..

No commits in common. "master" and "v0.13.0-unstable4" have entirely different histories.

33 changed files with 272 additions and 1239 deletions

View file

@ -1,4 +1,4 @@
{
"contributors": ["azukaar", "jwr1", "Jogai", "InterN0te", "catmandx", "revam"],
"contributors": ["azukaar", "jwr1", "Jogai", "InterN0te", "catmandx"],
"message": "We require contributors to sign our [Contributor License Agreement](https://github.com/azukaar/Cosmos-Server/blob/master/cla.md). In order for us to review and merge your code, add yourself to the .clabot file as contributor, as a way of signing the CLA."
}

View file

@ -1,23 +1,9 @@
## Version 0.13.1
- Fix a security issue with token (thansk @vncloudsco)
## Version 0.13.0
- Display container stacks as a group in the UI
- New Delete modal to delete services entirely
- Upload custom icons to containers
- improve backup file, by splitting cosmos out to a separate docker-compose.yml file
- Cosmos-networks now have specific names instead for generic names
- Display containers as stacks
- new Delete modal to delete services entirely
- cosmos-network now have container names instead for network names
- Fix issue where search bar reset when deleting volume/network
- Fix breadcrumbs in subpaths
- Remove graphs from non-admin UI to prevent errors
- Rewrite the overwriting container logic to fix race conditions
- Edit container user and devices from UI
- Fix bug where Cosmos Constellation's UDP ports by a TCP one
- Fix a bug with URL screen, where you can't delete a URL when there is a search
- Fix issue where negative network rate are reported
- Support array command and single device in docker-compose import
- Add default alerts... by default (was missing from the default config)
- disable few features liks Constellation, Backup and Monitoring when in install mode to reduce logs and prevent issues with the DB
## Version 0.12.6
- Fix a security issue with cross-domain APIs availability

View file

@ -78,7 +78,7 @@ export const checkHost = (host) => {
});
}
export const uploadImage = (file) => {
export const uploadBackground = (file) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({

View file

@ -199,10 +199,10 @@ let getDNS = (host) => {
});
}
let uploadImage = (file, name) => {
let uploadBackground = (file) => {
const formData = new FormData();
formData.append('image', file);
return wrap(fetch('/cosmos/api/upload/' + name, {
formData.append('background', file);
return wrap(fetch('/cosmos/api/background', {
method: 'POST',
body: formData
}));
@ -229,7 +229,7 @@ if(isDemo) {
isOnline = indexDemo.isOnline;
checkHost = indexDemo.checkHost;
getDNS = indexDemo.getDNS;
uploadImage = indexDemo.uploadImage;
uploadBackground = indexDemo.uploadBackground;
constellation = constellationDemo;
metrics = metricsDemo;
}
@ -247,5 +247,5 @@ export {
checkHost,
getDNS,
metrics,
uploadImage
uploadBackground
};

View file

@ -126,60 +126,6 @@
],
"all": {
"cosmos-cloud": [
{
"name": "Actual",
"description": "Automated finance tools are great, except when they arent. We provide you with tools that are quick to use, but ultimately you are in control. We help you learn, instead of dictating.",
"url": "",
"longDescription": "<p>Actual Budget is a super fast and privacy-focused app for managing your finances. At its heart is the well proven and much loved Envelope Budgeting methodology. You own your data and can do whatever you want with it.Featuring multi - device sync,optional end - to - end encryption and so much more. < /p>",
"tags": [
"Automated",
"finance",
"self-hosted"
],
"repository": "https://github.com/actualbudget/actual-server/",
"image": "https://hub.docker.com/r/actualbudget/actual-server",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Actual-server/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Actual-server/screenshots/2.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Actual-server/screenshots/3.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Actual-server/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Actual-server/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Audacity",
"description": "Audacity is a free, open-source, cross-platform audio software for multi-track editing and recording. It offers a comprehensive suite of tools for editing audio, adding effects, and supporting various file formats. With an intuitive interface, it is ideal for both beginners and professionals looking to produce high-quality audio on Windows, MacOS, and Linux.",
"url": "",
"longDescription": "<p>Audacity is a renowned open-source audio editing and recording software. Known for its versatility and ease of use, it is widely used by professionals and hobbyists alike for a range of audio editing tasks. Audacity supports multi-track editing, a host of audio effects, and extensive file format compatibility.</p><p>Its intuitive user interface allows users to easily record live audio, convert tapes and records into digital recordings or CDs, edit sound files, and more. Audacity also features powerful analysis tools and can be extended with a variety of plug-ins.</p><p>Available on multiple platforms including Windows, MacOS, and Linux, Audacity is the go-to solution for anyone looking to perform high-quality audio editing and recording without the need for expensive software.</p>",
"tags": [
"audio editing",
"recording",
"open-source",
"audacity",
"windows",
"linux",
"macos",
"multi-track",
"sound effects",
"file conversion",
"plug-ins"
],
"repository": "https://github.com/audacity/audacity",
"image": "https://hub.docker.com/r/linuxserver/audacity",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Audacity/screenshots/1.jpg"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Audacity/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Audacity/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Audiobookshelf",
"description": "Audiobookshelf is a self-hosted audiobook and podcast server.",
@ -268,70 +214,6 @@
"arm64"
]
},
{
"name": "Blender",
"description": "Blender is a free and open-source 3D creation suite supporting the entirety of the 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing, and motion tracking. It's versatile and powerful, equipped with a wide range of tools for creative professionals and hobbyists alike. Blender works across various platforms, offering advanced features for 3D graphics, animation, and more.",
"url": "",
"longDescription": "<p>Blender is a powerful open-source tool for 2D and 3D graphics, full-on animations, sculpting, rendering, compositing, motion tracking, and game creation. It stands out for its incredible versatility and comprehensive feature set. Blender is widely used for its advanced capabilities in modeling, texturing, and animation.</p><p>With a user-friendly interface and an extensive range of tools and features, Blender is suitable for both amateurs and professionals. It supports the entirety of the 3D pipeline and is continuously updated by a global community of contributors.</p><p>Blender is cross-platform, functioning seamlessly on Windows, MacOS, and Linux. It is the go-to tool for artists, designers, and animators who require a robust, flexible, and free tool for their creative endeavors.</p>",
"tags": [
"3d modeling",
"animation",
"rendering",
"open-source",
"blender",
"windows",
"linux",
"macos",
"game creation",
"motion tracking",
"compositing",
"visual effects"
],
"repository": "https://github.com/blender/blender",
"image": "https://hub.docker.com/r/linuxserver/blender",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Blender/screenshots/1.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Blender/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Blender/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Cachet",
"description": "Cachet, the open-source status page system.",
"url": "",
"longDescription": "<p>Cachet is an open-source status page system designed to help you keep track of your system status and share it with your user base. It is built to be responsive and works seamlessly across different devices.</p>",
"tags": [
"technical",
"administration",
"server",
"open-source",
"self-hosted",
"monitoring",
"uptime",
"service status",
"notifications",
"windows",
"linux",
"macos",
"docker"
],
"repository": "https://github.com/cachethq/cachet/",
"image": "https://hub.docker.com/r/vouu/cachet/",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Cachet/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Cachet/screenshots/2.jpeg"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Cachet/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Cachet/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Calibre",
"description": "Calibre is an open-source e-book management tool that allows users to organize, save, and manage e-books of various formats. It supports a wide range of e-book formats and offers features like format conversion, e-book syncing, news feeds from web sources, a built-in e-book viewer, and a server for online access.",
@ -627,9 +509,10 @@
"longDescription": "<p>Drupal is distributed with a license usually called open source. It means that in contrast to nearly all other publishing systems of the same magnitude, Drupal is free of charge. There is no fee to download or start using Drupal, and no yearly license payment to continue to use it.</p>",
"tags": [
"development",
"cms",
"repository management",
"code collaboration",
"self - hosted "
"issue tracking",
"self-hosted"
],
"repository": "https://github.com/bitnami/containers/",
"image": "https://hub.docker.com/r/bitnami/drupal",
@ -677,35 +560,6 @@
"arm64"
]
},
{
"name": "ESPHome",
"description": "ESPHome is a system to control your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems.",
"url": "",
"longDescription": "<p>ESPhome is a platform for creating custom firmware for ESP8266 and ESP32 chips using YAML configuration files. It turns these definitions into C++ firmware for easy integration of various smart devices.</p><p>ESPhome is seamlessly integrated with Home Assistant, an open-source home automation platform. With this integration, devices configured via ESPhome are automatically discovered and added to Home Assistant setups.</p><p>The platform boasts a robust community and extensive documentation. This, along with features like Over-The-Air (OTA) updates, makes ESPhome a favorite among smart home enthusiasts.</p>",
"tags": [
"development",
"open-source",
"cross-platform",
"ESPHome",
"Home Assistant",
"windows",
"linux",
"macos",
"docker",
"automation"
],
"repository": "https://github.com/esphome/esphome",
"image": "https://github.com/esphome/esphome/pkgs/container/esphome",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/ESPHome/screenshots/1.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/ESPHome/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/ESPHome/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Emby",
"description": "Emby(https://emby.media/) organizes video, music, live TV, and photos from personal media libraries and streams them to smart TVs, streaming boxes and mobile devices. This container is packaged as a standalone emby Media Server.",
@ -799,32 +653,6 @@
"arm64"
]
},
{
"name": "Fasten Health",
"description": "An open-source, self-hosted, personal/family electronic medical record aggregator (PHR), designed to integrate with 100,000's of insurances/hospitals & clinics.",
"url": "",
"longDescription": "<p>Fasten is an open-source, self-hosted, personal/family electronic medical record aggregator, designed to integrate with 100,000's of insurance companies, healthcare providers and laboratories. It securely connects patient healthcare providers together, creating a personal health record that never leaves the patient's hands without their consent.</p><p><b>This project is still in early stages. Expect non-functional features</b></p>",
"tags": [
"health",
"erm",
"insurance",
"healthcare",
"medical records"
],
"repository": "https://github.com/fastenhealth/fasten-onprem",
"image": "ghcr.io/fastenhealth/fasten-onprem",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Fasten/screenshots/fasten_dashboard.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Fasten/screenshots/fasten_main.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Fasten/screenshots/fasten_sources.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Fasten/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Fasten/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Filebrowser",
"description": "Filebrowser is an open-source, self-hosted web application for managing files within your server. It offers features like custom commands, user and permissions management, shareable links, built-in text editor, and media file previews. Filebrowser can be run on any platform that supports Go, including various Linux distributions, Windows, MacOS, and Docker.",
@ -887,62 +715,6 @@
"arm64"
]
},
{
"name": "Firefox",
"description": "Firefox is a feature-rich web browser focusing on user privacy and security. It provides a customizable user interface, a wide range of extensions, sync capabilities, and efficient performance. Firefox is designed for various platforms and is dedicated to promoting an open and accessible internet.",
"url": "",
"longDescription": "<p>Firefox, developed by Mozilla Foundation, is a leading open-source web browser known for its emphasis on privacy, security, and speed. It offers a user-friendly experience with extensive customization options, including a vast library of add-ons and themes.</p><p>Firefox is designed with advanced security features like Enhanced Tracking Protection to automatically block many trackers, safeguarding user data. Its efficient performance and developer-friendly tools make it a top choice for both general users and web developers. Firefox Sync allows seamless access to bookmarks, history, and tabs across devices.</p><p>Available on multiple platforms, including Windows, MacOS, Linux, Android, and iOS, Firefox is committed to empowering users with a fast, secure, and private browsing experience.</p>",
"tags": [
"web browser",
"privacy",
"security",
"mozilla",
"firefox",
"open source",
"extensions",
"cross-platform",
"sync",
"user-friendly",
"performance",
"developer tools"
],
"repository": "https://github.com/mozilla/firefox",
"image": "https://hub.docker.com/r/linuxserver/firefox",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Firefox/screenshots/1.webp"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Firefox/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Firefox/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Flarum",
"description": "Flarum is a modern and responsive open-source forum software written in PHP and Laravel.",
"url": "",
"longDescription": "Flarum is an open-source forum software designed to be simple, fast, and easy to use. It provides a modern and responsive user interface, making discussions and interactions within a community more engaging. Flarum is built using PHP and the Laravel framework, and it uses the Mithril JavaScript framework for its front-end.",
"tags": [
"forum software",
"open-source",
"PHP",
"community",
"laravel"
],
"repository": "https://github.com/mondediefr/docker-flarum",
"image": "https://hub.docker.com/r/mondedie/flarum",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Flarum/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Flarum/screenshots/2.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Flarum/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Flarum/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "FreshRSS",
"description": "FreshRSS is an open-source, self-hosted RSS feed aggregator that is lightweight and easy to use. Features include a responsive design, import/export OPML files, multiple themes, filters, categories, multi-user support, and extensibility with plugins. FreshRSS is compatible with Windows, Linux, MacOS, and Docker.",
@ -1319,8 +1091,8 @@
"macos",
"Immich"
],
"repository": "https://github.com/immich-app/immich",
"image": "ghcr.io/immich-app/immich-server",
"repository": "https://github.com/imagegenius/docker-immich",
"image": "https://github.com/imagegenius/docker-immich/pkgs/container/immich",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Immich/screenshots/2.webp",
@ -1465,33 +1237,6 @@
"arm64"
]
},
{
"name": "Jenkins",
"description": "Jenkins an open source automation server which enables developers around the world to reliably build, test, and deploy their software.",
"url": "",
"longDescription": "<p>Jenkins is typically run as a standalone application in its own process. The Jenkins WAR file bundles Winstone, a Jetty servlet container wrapper, and can be started on any operating system or platform with a version of Java supported by Jenkins.</p>",
"tags": [
"development",
"git",
"repository management",
"code collaboration",
"issue tracking",
"self-hosted"
],
"repository": "https://github.com/bitnami/containers/",
"image": "https://hub.docker.com/r/bitnami/jenkins",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Jenkins/screenshots/1.jpg",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Jenkins/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Jenkins/screenshots/2.jpg"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jenkins/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Jenkins/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Joplin",
"description": "Joplin is a free, open-source note-taking and to-do application that supports markdown and end-to-end encryption. It offers capabilities such as tagging, searching, and modifying notes, and can sync with various cloud platforms. It features an extensible plugin system, allowing for tailored functionality. Joplin is compatible across multiple platforms including Windows, Linux, MacOS, iOS, and Android.",
@ -1927,29 +1672,6 @@
"arm64"
]
},
{
"name": "N8n",
"description": "N8n (pronounced n-eight-n) helps you to connect any app with an API with any other, and manipulate its data with little or no code.",
"url": "",
"longDescription": "<p>N8n is an extendable workflow automation tool. With a fair-code distribution model, n8n will always have visible source code, be available to self-host, and allow you to add your own custom functions, logic and apps. n8n's node-based approach makes it highly versatile, enabling you to connect anything to everything.</p>",
"tags": [
"workflow",
"automation",
"self-hosted"
],
"repository": "https://github.com/n8n-io/n8n/",
"image": "https://docker.n8n.io/n8nio/n8n",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/N8n/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/N8n/screenshots/2.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/N8n/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/N8n/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "NZBGet",
"description": "NZBGet is a high-performance Usenet downloader that uses NZB files to fetch content. It is designed to use minimal system resources while offering features such as a web-interface, API, automatic post-processing, and RSS feed support. NZBGet works well on various platforms including low powered devices. Start using NZBGet for your Usenet downloads today!",
@ -2072,7 +1794,7 @@
},
{
"name": "Odoo",
"description": "Odoo is a suite of open source business apps that cover all your company needs: CRM, eCommerce, accounting, inventory, ...",
"description": "Redmine is a flexible project management web application. Written using the Ruby on Rails framework, it is cross-platform and cross-database.",
"url": "",
"longDescription": "<p>Odoo is a suite of web based open source business apps. The main Odoo Apps include an Open Source CRM, Website Builder, eCommerce, Warehouse Management.</p>",
"tags": [
@ -2178,27 +1900,6 @@
"arm64"
]
},
{
"name": "Openspeedtest",
"description": "HTML5 Internet Speed Test. no Flash or Java!",
"url": "",
"longDescription": "<p>SpeedTest by OpenSpeedTest™ is a Free and Open-Source HTML5 Network Performance Estimation Tool Written in Vanilla Javascript and only uses built-in Web APIs like XMLHttpRequest (XHR), HTML, CSS, JS, & SVG. No Third-Party frameworks or libraries are Required. All we need is a static web server like NGINX. This project was started in 2011 and moved to OpenSpeedTest.com dedicated Project/Domain Name in 2013.</p>",
"tags": [
"self-hosted",
"management"
],
"repository": "https://github.com/openspeedtest/Speed-Test",
"image": "https://hub.docker.com/r/openspeedtest/latest",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Openspeedtest/screenshots/1.gif"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Openspeedtest/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Openspeedtest/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Overseerr",
"description": "Overseerr is a request management and media discovery tool built to work with your existing Plex ecosystem.",
@ -2295,27 +1996,6 @@
"arm64"
]
},
{
"name": "Pingvin-share",
"description": "file sharing platform that combines lightness and beauty, perfect for seamless and efficient file sharing",
"url": "",
"longDescription": "<p>Pingvin Share is self-hosted file sharing platform and an alternative for WeTransfer.<br /><br /> ## ✨ Features<br /> <br /> - Share files using a link<br /> - Unlimited file size (restricted only by disk space)<br /> - Set an expiration date for shares<br /> - Secure shares with visitor limits and passwords<br /> - Email recipients<br /> - Integration with ClamAV for security scans</p>",
"tags": [
"share",
"file"
],
"repository": "https://github.com/stonith404/pingvin-share",
"image": "https://hub.docker.com/r/stonith404/pingvin-share",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Pingvin-share/screenshots/1.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Pingvin-share/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Pingvin-share/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Piped",
"description": "Piped is a privacy-focused open-source YouTube frontend that offers users an alternative to the official YouTube platform. It prioritizes user privacy and offers a range of features to enhance the viewing experience.",
@ -2567,61 +2247,6 @@
"arm64"
]
},
{
"name": "SABnzbd",
"description": "SABnzbd is an advanced Usenet downloader that automates the process of downloading and managing files from Usenet. It features a user-friendly web interface, API support, automatic post-processing, and RSS feed integration. Optimized for performance, SABnzbd runs smoothly on different platforms, including low-powered devices. Experience efficient Usenet downloading with SABnzbd!",
"url": "",
"longDescription": "<p>SABnzbd is an open-source binary newsreader and Usenet downloader. It simplifies the process of downloading from Usenet by automating the majority of tasks involved. SABnzbd is renowned for its ease of use, high-speed downloading capabilities, and extensive customization options.</p><p>With features like web-interface accessibility, API support, automatic post-processing, and RSS feed integration, SABnzbd stands out as a versatile tool for Usenet downloading. It is optimized to run efficiently on various hardware, including low-powered devices like the Raspberry Pi.</p><p>SABnzbd is cross-platform, available on Windows, MacOS, Linux, and other operating systems, making it accessible to a wide range of users. Embrace the power and simplicity of SABnzbd for your Usenet downloading needs.</p>",
"tags": [
"usenet",
"downloader",
"newsreader",
"sabnzbd",
"windows",
"linux",
"macos",
"raspberry pi",
"performance",
"rss feed",
"post-processing",
"open-source"
],
"repository": "https://github.com/sabnzbd/sabnzbd",
"image": "https://hub.docker.com/r/linuxserver/sabnzbd",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Sabnzbd/screenshots/1.webp"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sabnzbd/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Sabnzbd/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "SearXNG",
"description": "SearXNG is an open-source metasearch engine focused on privacy and user control.",
"url": "",
"longDescription": "SearXNG is a privacy-respecting, open-source metasearch engine that aggregates results from various search engines while respecting user privacy. It allows users to search without being tracked and provides customizable search preferences. SearXNG can be self-hosted or used on public instances, giving users control over their search experience.",
"tags": [
"search engine",
"privacy",
"open-source",
"metasearch"
],
"repository": "https://github.com/searxng/searxng",
"image": "https://hub.docker.com/r/searxng/searxng",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/SearXNG/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/SearXNG/screenshots/2.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/SearXNG/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/SearXNG/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Sonarqube",
"description": "Empower development teams with a code quality, security and static analysis solution that deeply integrates into your enterprise environment.",
@ -2788,100 +2413,6 @@
"arm64"
]
},
{
"name": "Tautulli",
"description": "Tautulli A Python based monitoring and tracking tool for Plex Media Server.",
"url": "",
"longDescription": "<p>Tautulli is a 3rd party application that you can run alongside your Plex Media Server to monitor activity and track various statistics. Most importantly, these statistics include what has been watched, who watched it, when and where they watched it, and how it was watched.</p>",
"tags": [
"media",
"movies",
"streaming",
"tv",
"music",
"photos",
"videos",
"audiobooks",
"podcasts",
"dlna",
"chromecast",
"android",
"ios",
"roku",
"firetv",
"smarttv",
"web",
"browser",
"kodi",
"emby",
"plex",
"media browser",
"media server",
"media streaming",
"media player",
"media center",
"media management",
"media organizer",
"media collection",
"media library",
"media manager",
"media sharing",
"media transcoding",
"media casting",
"media casting",
"media casting"
],
"repository": "https://github.com/Tautulli/Tautulli/",
"image": "https://hub.docker.com/r/linuxserver/tautulli",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/screenshots/1.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/screenshots/2.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/screenshots/3.png",
"https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/screenshots/4.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Tautulli/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "Threadfin",
"description": "M3U Proxy for Plex DVR and Emby/Jellyfin Live TV. Based on xTeVe.",
"url": "",
"longDescription": "<p>Threadfin is a M3U proxy server for Plex, Emby, Jellyfin and any client and provider which supports the .TS and .M3U8 (HLS) streaming formats.</p><p>Threadfin emulates a SiliconDust HDHomeRun OTA tuner, which allows it to expose IPTV style channels to software, which would not normally support it. This Docker image includes the following packages and features:</p><ul><li>Threadfin (Linux) x86 64 bit ffmpeg Support Runs as unprivileged user</li></ul>",
"tags": [
"media",
"iptv",
"tvguide",
"open-source",
"self-hosted",
"web application",
"plex",
"emby",
"jellyfin",
"request system",
"media content",
"windows",
"linux",
"macos",
"docker"
],
"repository": "https://github.com/Threadfin/Threadfin",
"image": "https://hub.docker.com/r/fyb3roptik/threadfin",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Threadfin/screenshots/1.png"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Threadfin/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Threadfin/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64",
"arm/v7",
"arm/v8"
]
},
{
"name": "Transmission",
"description": "Transmission is an open-source, cross-platform BitTorrent client. It is widely praised for its minimal resource usage, ease-of-use, and performance. Transmission provides features like built-in encryption, webseed support, peer exchange, automatic port mapping, and individual torrent speed limit settings. It supports Windows, Linux, MacOS, and also offers a web interface for remote control.",
@ -3038,28 +2569,6 @@
"arm64"
]
},
{
"name": "Webtop",
"description": "Webtop - Alpine, Ubuntu, Fedora, and Arch based containers containing full desktop environments in officially supported flavors accessible via any modern web browser",
"url": "",
"longDescription": "<p>Webtop - Alpine, Ubuntu, Fedora, and Arch based containers containing full desktop environments in officially supported flavors accessible via any modern web browser</p>",
"tags": [
"Desktop",
"VNC",
"KASM"
],
"repository": "https://github.com/kasmtech",
"image": "https://hub.docker.com/r/linuxserver/docker-webtop",
"screenshots": [
"https://azukaar.github.io/cosmos-servapps-official/servapps/Webtop/screenshots/1.jpg"
],
"icon": "https://azukaar.github.io/cosmos-servapps-official/servapps/Webtop/icon.png",
"compose": "https://azukaar.github.io/cosmos-servapps-official/servapps/Webtop/cosmos-compose.json",
"supported_architectures": [
"amd64",
"arm64"
]
},
{
"name": "WordPress",
"description": "WordPress is a free, open-source content management system based on PHP and MySQL. Known for its flexibility and ease-of-use, it supports a range of website types with thousands of plugins and themes. WordPress features a robust administrative dashboard for website creation, modification, and management. It is widely supported across various hosting platforms.",

View file

@ -27,8 +27,7 @@ const dashboard = {
type: 'item',
url: '/cosmos-ui/monitoring',
icon: DashboardOutlined,
breadcrumbs: false,
adminOnly: true
breadcrumbs: false
},
{
id: 'market',

View file

@ -13,7 +13,6 @@ import { CosmosCheckbox } from '../users/formShortcuts';
import { Field } from 'formik';
import MiniPlotComponent from '../../dashboard/components/mini-plot';
import ImageWithPlaceholder from '../../../components/imageWithPlaceholder';
import UploadButtons from '../../../components/fileUpload';
const info = {
backgroundColor: 'rgba(0, 0, 0, 0.1)',

View file

@ -316,10 +316,10 @@ const ConfigManagement = () => {
<MainCard title="Appearance">
<Grid container spacing={3}>
<Grid item xs={12}>
{!uploadingBackground && formik.values.Background && <img src=
{formik.values.Background} alt="preview seems broken. Please re-upload."
width={285} />}
{uploadingBackground && <Skeleton variant="rectangular" width={285} height={140} />}
{!uploadingBackground && formik.values.Background && <img src=
{formik.values.Background} alt="preview seems broken. Please re-upload."
width={285} />}
{uploadingBackground && <Skeleton variant="rectangular" width={285} height={140} />}
<Stack spacing={1} direction="row">
<UploadButtons
accept='.jpg, .png, .gif, .jpeg, .webp, .bmp, .avif, .tiff, .svg'
@ -327,8 +327,8 @@ const ConfigManagement = () => {
OnChange={(e) => {
setUploadingBackground(true);
const file = e.target.files[0];
API.uploadImage(file, "background").then((data) => {
formik.setFieldValue('Background', data.data.path);
API.uploadBackground(file).then((data) => {
formik.setFieldValue('Background', "/cosmos/api/background/" + data.data.extension.replace(".", ""));
setUploadingBackground(false);
});
}}

View file

@ -206,10 +206,10 @@ const ProxyManagement = () => {
style: {minWidth: '70px'} },
{ title: '', clickable:true, field: (r, k) => <RouteActions
route={r}
routeKey={routes.indexOf(r)}
up={(event) => up(event, routes.indexOf(r))}
down={(event) => down(event, routes.indexOf(r))}
deleteRoute={(event) => deleteRoute(event, routes.indexOf(r))}
routeKey={k}
up={(event) => up(event, k)}
down={(event) => down(event, k)}
deleteRoute={(event) => deleteRoute(event, k)}
/>,
style: {
textAlign: 'right',

View file

@ -124,8 +124,6 @@ const HomePage = () => {
}
function getMetrics() {
if(!isAdmin) return;
API.metrics.get([
"cosmos.system.cpu.0",
"cosmos.system.ram",
@ -262,7 +260,7 @@ const HomePage = () => {
let latestCPU, latestRAM, latestRAMRaw, maxRAM, maxRAMRaw = 0;
if(isAdmin && metrics) {
if(metrics) {
if(metrics["cosmos.system.cpu.0"] && metrics["cosmos.system.cpu.0"].Values && metrics["cosmos.system.cpu.0"].Values.length > 0)
latestCPU = metrics["cosmos.system.cpu.0"].Values[metrics["cosmos.system.cpu.0"].Values.length - 1].Value;
@ -338,7 +336,7 @@ const HomePage = () => {
</Stack>
<Grid2 container spacing={2} style={{ zIndex: 2 }}>
{isAdmin && coStatus && !coStatus.MonitoringDisabled && (<>
{coStatus && !coStatus.MonitoringDisabled && (<>
{isMd && !metrics && (<>
<Grid2 item xs={12} sm={6} md={6} lg={3} xl={3} xxl={3} key={'000'}>
<Box className='app' style={{height: '106px', borderRadius: 5, ...appColor }}>

View file

@ -186,6 +186,7 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
// convert to the proper format
if (doc.services) {
Object.keys(doc.services).forEach((key) => {
// convert volumes
if (doc.services[key].volumes) {
if (Array.isArray(doc.services[key].volumes)) {
@ -272,30 +273,6 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
}
}
// convert devices
if (doc.services[key].devices) {
console.log(1)
if (Array.isArray(doc.services[key].devices)) {
console.log(2)
let devices = [];
doc.services[key].devices.forEach((device) => {
if(device.indexOf(':') === -1) {
devices.push(device + ':' + device);
} else {
devices.push(device);
}
});
doc.services[key].devices = devices;
}
}
// convert command
if (doc.services[key].command) {
if (typeof doc.services[key].command !== 'string') {
doc.services[key].command = doc.services[key].command.join(' ');
}
}
// ensure container_name
if (!doc.services[key].container_name) {
doc.services[key].container_name = key;
@ -675,11 +652,9 @@ const DockerComposeImport = ({ refresh, dockerComposeInit, installerInit, defaul
Config: {
Env: value.environment || [],
Labels: value.labels || {},
User: value.user || '',
},
HostConfig: {
RestartPolicy: {},
Devices: value.devices || [],
}
}}
OnChange={(containerInfo) => {

View file

@ -69,9 +69,6 @@ const NewDockerServiceForm = () => {
image: containerInfo.Config.Image,
environment: containerInfo.Config.Env,
labels: containerInfo.Config.Labels,
devices: containerInfo.HostConfig.Devices ? containerInfo.HostConfig.Devices.map((device) => {
return `${device.PathOnHost}:${device.PathInContainer}:`;
}) : [],
expose: containerInfo.Config.ExposedPorts,
tty: containerInfo.Config.Tty,
stdin_open: containerInfo.Config.OpenStdin,
@ -145,17 +142,9 @@ const NewDockerServiceForm = () => {
Name: values.name,
Env: values.envVars,
Labels: values.labels,
User: values.user,
},
HostConfig: {
...containerInfo.HostConfig,
Devices: values.devices.map((device) => {
return {
PathOnHost: device.split(':')[0],
PathInContainer: device.split(':')[1],
CgroupPermissions: 'rwm',
};
}),
RestartPolicy: {
Name: values.restartPolicy,
},

View file

@ -10,7 +10,6 @@ import RestartModal from '../../config/users/restart';
import GetActions from '../actionBar';
import { ServAppIcon } from '../../../utils/servapp-icon';
import MiniPlotComponent from '../../dashboard/components/mini-plot';
import UploadButtons from '../../../components/fileUpload';
const info = {
backgroundColor: 'rgba(0, 0, 0, 0.1)',
@ -88,25 +87,6 @@ const ContainerOverview = ({ containerInfo, config, refresh, updatesAvailable, s
"dead": <Chip label="Dead" color="error" />,
})[State.Status]}
</div>
<UploadButtons
accept='.jpg, .png, .gif, .jpeg, .webp, .bmp, .avif, .tiff, .svg'
label="icon"
OnChange={(e) => {
const file = e.target.files[0];
setIsUpdating(true);
API.uploadImage(file, "servapp-" + Name.replace('/', '')).then((data) => {
API.docker.updateContainer(Name.replace('/', ''), {
labels: {
...Config.Labels,
"cosmos-icon": data.data.path,
}
})
.then(() => {
refreshAll();
});
});
}}
/>
</Stack>
<Stack spacing={2} style={{ width: '100%' }} >

View file

@ -3,7 +3,7 @@ import { Field, Formik } from 'formik';
import { Button, Stack, Grid, MenuItem, TextField, IconButton, FormHelperText, useMediaQuery, useTheme, Alert, FormControlLabel, Checkbox } from '@mui/material';
import MainCard from '../../../components/MainCard';
import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect }
from '../../config/users/formShortcuts';
from '../../config/users/formShortcuts';
import { DeleteOutlined, PlusCircleOutlined } from '@ant-design/icons';
import * as API from '../../../api';
import { LoadingButton } from '@mui/lab';
@ -15,28 +15,20 @@ const containerInfoFrom = (values) => {
values.labels.forEach((label) => {
labels[label.key] = label.value;
});
const devices = values.devices.map((device) => {
return `${device.key}:${device.value}`;
});
const envVars = values.envVars.map((envVar) => {
return `${envVar.key}=${envVar.value}`;
});
const realvalues = {
...values,
envVars: envVars,
labels: labels,
devices: devices,
};
realvalues.interactive = realvalues.interactive ? 2 : 1;
return realvalues;
}
const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refresh, newContainer, OnForceSecure }) => {
const DockerContainerSetup = ({noCard, containerInfo, installer, OnChange, refresh, newContainer, OnForceSecure}) => {
const restartPolicies = [
['no', 'No Restart'],
['always', 'Always Restart'],
@ -48,7 +40,7 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
const isMobile = useMediaQuery(theme.breakpoints.down('sm'));
const padding = isMobile ? '6px 4px' : '12px 10px';
const [latestImage, setLatestImage] = React.useState(containerInfo.Config.Image);
const wrapCard = (children) => {
if (noCard) return children;
return <MainCard title="Docker Container Setup">
@ -63,19 +55,13 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
name: containerInfo.Name.replace('/', ''),
image: containerInfo.Config.Image,
restartPolicy: containerInfo.HostConfig.RestartPolicy.Name,
user: containerInfo.Config.User,
envVars: containerInfo.Config.Env.map((envVar) => {
const [key, value] = envVar.split(/=(.*)/s);
return { key, value };
return { key, value };
}),
labels: Object.keys(containerInfo.Config.Labels).map((key) => {
return { key, value: containerInfo.Config.Labels[key] };
}),
devices: containerInfo.HostConfig.Devices ? containerInfo.HostConfig.Devices.map((device) => {
return (typeof device == "string") ?
{ key: device.split(":")[0], value: (device.split(":")[1] || device.split(":")[0]) }
: { key: device.PathOnHost, value: device.PathInContainer };
}) : [],
interactive: containerInfo.Config.Tty && containerInfo.Config.OpenStdin,
}}
enableReinitialize
@ -102,12 +88,12 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
return errors;
}}
onSubmit={async (values, { setErrors, setStatus, setSubmitting }) => {
if (values.image !== latestImage) {
setPullRequest(() => ((cb) => API.docker.pullImage(values.image, cb, true)));
if(values.image !== latestImage) {
setPullRequest(() => ((cb) => API.docker.pullImage(values.image,cb, true)));
return;
}
if (newContainer) return false;
if(newContainer) return false;
delete values.name;
setSubmitting(true);
@ -120,11 +106,11 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
setSubmitting(false);
refresh && refresh();
}
).catch((err) => {
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
});
).catch((err) => {
setStatus({ success: false });
setErrors({ submit: err.message });
setSubmitting(false);
});
}}
>
{(formik) => (
@ -139,45 +125,39 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
/>
<Stack spacing={2}>
{wrapCard(<>
{containerInfo.State && containerInfo.State.Status !== 'running' && (
<Alert severity="warning" style={{ marginBottom: '15px' }}>
This container is not running. Editing any settings will cause the container to start again.
</Alert>
)}
{containerInfo.State && containerInfo.State.Status !== 'running' && (
<Alert severity="warning" style={{ marginBottom: '15px' }}>
This container is not running. Editing any settings will cause the container to start again.
</Alert>
)}
<Grid container spacing={4}>
{!installer && <>
{!installer && <>
{newContainer && <CosmosInputText
name="name"
label="Name"
placeholder="Name"
formik={formik}
/>}
/>}
<CosmosInputText
name="image"
label="Image"
placeholder="Image"
formik={formik}
/>
/>
<CosmosSelect
name="restartPolicy"
label="Restart Policy"
placeholder="Restart Policy"
options={restartPolicies}
formik={formik}
/>
<CosmosInputText
name="user"
label="User"
placeholder="User"
formik={formik}
/>
/>
<CosmosCheckbox
name="interactive"
label="Interactive Mode"
formik={formik}
/>
{OnForceSecure && <Grid item xs={12}>
<Checkbox
<Checkbox
type="checkbox"
as={FormControlLabel}
control={<Checkbox size="large" />}
@ -190,14 +170,14 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
OnForceSecure(e.target.checked);
}}
/>
</Grid>}
</Grid>}
</>}
<CosmosFormDivider title={'Environment Variables'} />
<Grid item xs={12}>
{formik.values.envVars.map((envVar, idx) => (
<Grid container key={idx}>
<Grid item xs={5} style={{ padding }}>
<Grid item xs={5} style={{padding}}>
<TextField
label="Key"
fullWidth
@ -209,7 +189,7 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
}}
/>
</Grid>
<Grid item xs={6} style={{ padding }}>
<Grid item xs={6} style={{padding}}>
<TextField
fullWidth
label="Value"
@ -221,7 +201,7 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
}}
/>
</Grid>
<Grid item xs={1} style={{ padding }}>
<Grid item xs={1} style={{padding}}>
<IconButton
fullWidth
variant="outlined"
@ -251,12 +231,11 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
Add
</ResponsiveButton>
</Grid>
<CosmosFormDivider title={'Labels'} />
<Grid item xs={12}>
{formik.values.labels.map((label, idx) => (
<Grid container key={idx}>
<Grid item xs={5} style={{ padding }}>
<Grid item xs={5} style={{padding}}>
<TextField
fullWidth
label="Key"
@ -268,7 +247,7 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
}}
/>
</Grid>
<Grid item xs={6} style={{ padding }}>
<Grid item xs={6} style={{padding}}>
<TextField
label="Value"
fullWidth
@ -280,7 +259,7 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
}}
/>
</Grid>
<Grid item xs={1} style={{ padding }}>
<Grid item xs={1} style={{padding}}>
<IconButton
fullWidth
variant="outlined"
@ -310,67 +289,6 @@ const DockerContainerSetup = ({ noCard, containerInfo, installer, OnChange, refr
Add
</ResponsiveButton>
</Grid>
<CosmosFormDivider title={'Devices'} />
<Grid item xs={12}>
{formik.values.devices.map((device, idx) => (
<Grid container key={idx}>
<Grid item xs={5} style={{ padding }}>
<TextField
fullWidth
label="Host Path"
value={device.key}
onChange={(e) => {
const newDevices = [...formik.values.devices];
newDevices[idx].key = e.target.value;
formik.setFieldValue('devices', newDevices);
}}
/>
</Grid>
<Grid item xs={6} style={{ padding }}>
<TextField
label="Container Path"
fullWidth
value={device.value}
onChange={(e) => {
const newDevices = [...formik.values.devices];
newDevices[idx].value = e.target.value;
formik.setFieldValue('devices', newDevices);
}}
/>
</Grid>
<Grid item xs={1} style={{ padding }}>
<IconButton
fullWidth
variant="outlined"
color="error"
onClick={() => {
const newDevices = [...formik.values.devices];
newDevices.splice(idx, 1);
formik.setFieldValue('devices', newDevices);
}}
>
<DeleteOutlined />
</IconButton>
</Grid>
</Grid>
))}
<ResponsiveButton
variant="outlined"
color="primary"
size='large'
onClick={() => {
const newDevices = [...formik.values.devices];
newDevices.push({ key: '', value: '' });
formik.setFieldValue('devices', newDevices);
}}
startIcon={<PlusCircleOutlined />}
>
Add
</ResponsiveButton>
</Grid>
</Grid>
</>)}
{!newContainer && <MainCard>

View file

@ -283,16 +283,8 @@ const ServApps = ({stack}) => {
<Alert severity="info">Update are available for {Object.keys(updatesAvailable).join(', ')}</Alert>
</Item>
</Grid2>}
{servApps && Object.values(servAppsStacked)
.filter(app => {
if (search.length < 2) return true;
if (app.name.toLowerCase().includes(search.toLowerCase())) return true;
if (app.app.Image.toLowerCase().includes(search.toLowerCase())) return true;
if (app.app.Id.toLowerCase().includes(search.toLowerCase())) return true;
if (app.apps.find((app) => app.Names[0].toLowerCase().includes(search.toLowerCase()))) return true;
})
.map((app) => {
return <Grid2 sx={{...gridAnim}} xs={12} sm={6} md={6} lg={6} xl={4} key={app.app.Id} item>
{servApps && Object.values(servAppsStacked).filter(app => search.length < 2 || app.name.toLowerCase().includes(search.toLowerCase())).map((app) => {
return <Grid2 sx={{...gridAnim}} xs={12} sm={6} md={6} lg={6} xl={4} key={app.Id} item>
<Item>
<Stack justifyContent='space-around' direction="column" spacing={2} padding={2} divider={<Divider orientation="horizontal" flexItem />}>
<Stack direction="column" spacing={0} alignItems="flex-start">

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.13.1",
"version": "0.13.0-unstable4",
"description": "",
"main": "test-server.js",
"bugs": {
@ -80,7 +80,7 @@
"build": "sh build.sh",
"dev": "npm run build && npm run start",
"dockerdevbuild": "docker build -f dockerfile.local --tag cosmos-dev .",
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run --cap-add NET_ADMIN -d -p 7200:443 -p 80:80 -p 53:53 -p 443:443 -p 4242:4242/udp -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
"dockerdevrun": "docker stop cosmos-dev; docker rm cosmos-dev; docker run --cap-add NET_ADMIN -d -p 7200:443 -p 80:80 -p 53:53 -p 443:443 -p 4242:4242 -e DOCKER_HOST=tcp://host.docker.internal:2375 -e COSMOS_MONGODB=$MONGODB -e COSMOS_LOG_LEVEL=DEBUG -v /:/mnt/host --restart=unless-stopped -h cosmos-dev --name cosmos-dev cosmos-dev",
"dockerdev": "npm run client-build && npm run dockerdevbuild && npm run dockerdevrun",
"demo": "vite build --base=/cosmos-ui/ --mode demo",
"devdemo": "vite --mode demo"

View file

@ -11,8 +11,6 @@
<a href="https://github.com/BlackrazorNZ"><img src="https://avatars.githubusercontent.com/BlackrazorNZ" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/owengraven"><img src="https://avatars.githubusercontent.com/owengraven" style="border-radius:48px" width="48" height="48" alt="Owen" title="Owen" /></a>
<a href="https://github.com/DGAzr"><img src="https://avatars.githubusercontent.com/DGAzr" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/LostPoE"><img src="https://avatars.githubusercontent.com/LostPoE" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/eldergod1800"><img src="https://avatars.githubusercontent.com/eldergod1800" style="border-radius:48px" width="48" height="48" alt="null" title="null" /></a>
<a href="https://github.com/Dimtar"><img src="https://avatars.githubusercontent.com/Dimtar" style="border-radius:48px" width="48" height="48" alt="Daniel C" title="Daniel C" /></a>
</p><!-- /sponsors -->
@ -21,7 +19,7 @@
[![DiscordLink](https://img.shields.io/discord/1083875833824944188?label=Discord&logo=Discord&style=flat-square)](https://discord.gg/PwMWwsrwHA) ![CircleCI](https://img.shields.io/circleci/build/github/azukaar/Cosmos-Server?token=6efd010d0f82f97175f04a6acf2dae2bbcc4063c&style=flat-square) [![Docker Pulls](https://img.shields.io/docker/pulls/azukaar/cosmos-server?style=flat-square)](https://hub.docker.com/r/azukaar/cosmos-server)
☁️ Cosmos is the most secure and easy way to self-host a Home Server. It acts as a secure gateway to your application, as well as a server manager. It aims to solve the increasingly worrying problem of vulnerable self-hosted applications and personal servers.
☁️ Cosmos is the most secure and easy way to selfhost a Home Server. It acts as a secure gateway to your application, as well as a server manager. It aims to solve the increasingly worrying problem of vulnerable self-hosted applications and personal servers.
<p align="center">
<br/>
@ -41,9 +39,9 @@
![screenshot1](./screenshot1.png)
Whether you have a **server**, a **NAS**, or a **Raspberry Pi** with applications such as **Plex**, **HomeAssistant** or even a blog, Cosmos is the perfect solution to run and secure them all. Simply install Cosmos on your server and connect to your applications through it to enjoy built-in security and robustness for all your services, right out of the box.
Whether you have a **server**, a **NAS**, or a **Raspberry Pi** with applications such as **Plex**, **HomeAssistant** or even a blog, Cosmos is the perfect solution torun and secure them all. Simply install Cosmos on your server and connect to your applications through it to enjoy built-in security and robustness for all your services, right out of the box.
Cosmos is:
Cosmos is a:
* **App Store** 📦📱 To easily install and manage your applications, with simple installers, automatic updates and security checks. This works alongside manual installation methods, such as importing docker-compose files, or the docker CLI
* **Customizable Homepage** 🏠🖼 To access all your applications from a single place, with a beautiful and customizable UI
@ -73,14 +71,14 @@ Cosmos has a few key differences with other alternatives such as YunoHost, Unrai
<p align="center" style="color:grey">
<img src="./compare.png" /><br />
* Only containers created with their UI, with no customization, ** No alerts
* Only containers created with their UI, with no customization, * No alerts
</p>
* **Security**: Cosmos has a unique strong focus on securing your application with exclusive features such as the smart-shield. It has 2FA, OpenID, anti-DDOS, and other security features built-in. It also has a strong focus on privacy, with the latest encryption methods and a strong focus on data protection. Unlike any other solutions, it assumes the software you run are not trustworthy, and protects you from them.
* **Power-user friendly**: Some of those alternatives can feel a bit "limiting" to someone who knows what they are doing. On the other hand, while Cosmos is designed to be easy to use, it is also powerful and flexible. It is designed to be used by both new and experienced users, and to integrate into your existing home server, the already existing applications you have, and the new ones you want to install. It can even be used from the terminal if you want to!
* **Flexible**: Unlike the alternatives, Cosmos is not exclusively focused around its app-store. Instead, it lets you freely install any application any way you want, and manage them from the UI, from Portainer, or from docker directly. Any of those applications will still be integrated into Cosmos and will also benefit from all the security features, Let's Encrypt, etc..
* **Learning experience**: If you are new to self-hosting, using a software that hides all the complexity from you can prevent you from learning how to properly manage your server. Cosmos is designed to be easy to use, but also to be a learning experience. It does not hide things away but instead guides you and incentivizes learning more about the tools you are using.
* **Power-user friendly**: Some of those alternatives can feel a bit "limiting" to someone who kows what they are doing. On the other hand, while Cosmos is designed to be easy to use, it is also powerful and flexible. It is designed to be used by both new and experienced users, and to integrate into your existing home server, the already existing applications you have, and the new ones you want to install. It can even be used from the terminal if you want to!
* **Flexible**: Unlike the alternatives, Cosmos is not exclusively focused around its app-store. Instead, it lets you freely install any application any way you want, and manage them from the UI, from Portainer, or from docker directly. Any of those applications will still be integrated into Cosmos and will also benefit from all the security features, Let's Encrypt, etc...
* **Learning experience**: If you are new to selfhosting, using a software that hides all the complexity from you can prevent you from learning how to properly manage your server. Cosmos is designed to be easy to use, but also to be a learning experience. It does not hide things away but instead guides you and proposes you to learn more about the tools you are using.
* **No vendor-locking**: Unlike solution that tightly couple their applications to the containers, Cosmos can manage apps created from anywhere all the same, and converting an existing container to a Cosmos app is as simple as adding a URL in the UI. You can also migrate out of Cosmos at any time, as it only uses vanilla docker containers.
You might also wonder about **cloudflare proxy** and **cloudflare tunnel** when it comes to security. But unlike popular beliefs those are not solutions on their own as:
@ -126,11 +124,13 @@ Another example:
Additionally, because every new self-hosted applications re-implement **crucial systems** such as authentication **from scratch** everytime, the **large majority** of them are very succeptible to being **hacked without too much trouble**. This is very bad because not only Docker containers are not isolated, but they also run as **root** by default, which means it can **easily be used** to offer access to your entire server or even infrastructure.
Most tools currently used to self-host **not specifically designed to be secure for your scenario**. Entreprise tools such as Traefik, NGinx, etc... Are designed for different use-cases that assume that the code you are running behind them is **trustworthy**. But who knows what server apps you might be running? On top of that, a lot of reverse-proxies and security tools lock important security features behind 3 to 4 figures business subscriptions that are not realistic for self-hosting.
Most tools currently used to self-host **not specifically designed to be secure for your scenario**. Entreprise tools such as Traefik, NGinx, etc... Are designed for different use-cases that assume that the code you are running behind them is **trustworthy**. But who knows what server apps you might be running? On top of that, a lot of reverse-proxies and security tools lock important security features behind 3 to 4 figures business subscriptions that are not realistic for selfhosting.
If you have any further questions, feel free to join our [Discord](https://discord.gg/PwMWwsrwHA)!
**Disclaimer**: _Cosmos is still in early Alpha stage, please be careful when you use it. It is not (yet, at least ;p) a replacement for proper control and mindfulness of your own security._
```
Disclaimer: Cosmos is still in early Alpha stage, please be careful when you use it. It is not (yet, at least ;p) a replacement for proper control and mindfulness of your own security.
```
# Let's Encrypt
@ -146,7 +146,7 @@ Authentication is very hard (how do you check the password match? What encryptio
Cosmos is using the Apache 2.0 Licence with the Commons Clause 1.0. This is a common clause among open source infrastructure software, such as databases, reverse proxies, etc...
TL;DR: You can use it freely. You can also fork it and redistribute it, But you are not allowed to sell it, a derivative or to sell a service based on it (ex. SaaS or PaaS).
The TL;DR is: You can use it freely. You can also fork it and redistribute it, But you are not allowed to sell it, a derivative or to sell a service based on it (ex. SaaS or PaaS).
Note that **you are allowed** to use it to host a monetized business website, a blog etc... as long as your business does not involve selling Cosmos or its features.
@ -154,13 +154,13 @@ Note that **you are allowed** to use it to host a monetized business website, a
Installation is simple using Docker:
```sh
```
docker run -d -p 80:80 -p 443:443 -p 4242:4242/udp --privileged --name cosmos-server -h cosmos-server --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /:/mnt/host -v /var/lib/cosmos:/config azukaar/cosmos-server:latest
```
### DO NOT USE UNRAID TEMPLATES OR PORTAINER STACKS TO INSTALL COSMOS. IT WILL NOT WORK PROPERLY.
#### DO NOT USE UNRAID TEMPLATES OR PORTAINER STACKS TO INSTALL COSMOS. IT WILL NOT WORK PROPERLY.
In this command, `-v /:/mnt/host` is optional and allow to manage folders from Cosmos, you can remove it if you don't want it but you will have to create your container's bind folders manually.
in this command, `-v /:/mnt/host` is optional and allow to manage folders from Cosmos, you can remove it if you don't want it but you will have to create your container's bind folders manually.
`--privileged` is also optional, but it is required if you use hardening software like AppArmor or SELinux, as they restrict access to the docker socket. It is also required for Constellation to work. If you don't want to use it, you can add the following capabilities: NET_ADMIN for Constellation.
@ -168,10 +168,10 @@ Once installed, simply go to `http://your-server-ip` and follow the instructions
Port 4242 is a UDP port used for the Constellation VPN.
Make sure you expose the right ports (by default 80 / 443). It is best to keep those ports intacts, as Cosmos is meant to run as your reverse proxy. Trying to setup Cosmos behind another reverse proxy is possible but will only create headaches.
make sure you expose the right ports (by default 80 / 443). It is best to keep those ports intacts, as Cosmos is meant to run as your reverse proxy. Trying to setup Cosmos behind another reverse proxy is possible but will only create headaches.
You also need to keep the docker socket mounted, as Cosmos needs to be able to manage your containers.
You can tweak the config file accordingly. Some settings can be changed before end with env var. [see here](https://cosmos-cloud.io/doc/9%20Other%20Setups/#env-var).
If you are having issues with the installation, please contact us on [Discord](https://discord.gg/PwMWwsrwHA)!
if you are having issues with the installation, please contact us on [Discord](https://discord.gg/PwMWwsrwHA)!

View file

@ -126,7 +126,6 @@ func CRON() {
s.Every(1).Day().At("00:00").Do(func() {
utils.CleanupByDate("notifications")
utils.CleanupByDate("events")
imageCleanUp()
})
s.Start()
}()

117
src/background.go Normal file
View file

@ -0,0 +1,117 @@
package main
import (
"io/ioutil"
"os"
"net/http"
"path/filepath"
"io"
"encoding/json"
"github.com/gorilla/mux"
"github.com/azukaar/cosmos-server/src/utils"
)
var validExtensions = map[string]bool{
".jpg": true,
".jpeg": true,
".png": true,
".gif": true,
".bmp": true,
".svg": true,
".webp": true,
".tiff": true,
".avif": true,
}
func UploadBackground(w http.ResponseWriter, req *http.Request) {
if utils.AdminOnly(w, req) != nil {
return
}
if(req.Method == "POST") {
// parse the form data
err := req.ParseMultipartForm(1 << 20)
if err != nil {
utils.HTTPError(w, "Error parsing form data", http.StatusInternalServerError, "FORM001")
return
}
// retrieve the file part of the form
file, header, err := req.FormFile("background")
if err != nil {
utils.HTTPError(w, "Error retrieving file from form data", http.StatusInternalServerError, "FORM002")
return
}
defer file.Close()
// get the file extension
ext := filepath.Ext(header.Filename)
if !validExtensions[ext] {
utils.HTTPError(w, "Invalid file extension " + ext, http.StatusBadRequest, "FILE001")
return
}
// create a new file in the config directory
dst, err := os.Create(utils.CONFIGFOLDER + "background" + ext)
if err != nil {
utils.HTTPError(w, "Error creating destination file", http.StatusInternalServerError, "FILE004")
return
}
defer dst.Close()
// copy the uploaded file to the destination file
if _, err := io.Copy(dst, file); err != nil {
utils.HTTPError(w, "Error writing to destination file", http.StatusInternalServerError, "FILE005")
return
}
// return a response to the client
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
"data": map[string]interface{}{
"filename": header.Filename,
"size": header.Size,
"extension": ext,
},
})
} else {
utils.Error("UploadBackground: Method not allowed - " + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
return
}
}
func GetBackground(w http.ResponseWriter, req *http.Request) {
if utils.LoggedInOnly(w, req) != nil {
return
}
vars := mux.Vars(req)
ext := vars["ext"]
if !validExtensions["." + ext] {
utils.HTTPError(w, "Invalid file extension", http.StatusBadRequest, "FILE001")
return
}
if(req.Method == "GET") {
// get the background image
bg, err := ioutil.ReadFile(utils.CONFIGFOLDER + "background." + ext)
if err != nil {
utils.HTTPError(w, "Error reading background image", http.StatusInternalServerError, "FILE003")
return
}
// return a response to the client
w.Header().Set("Content-Type", "image/" + ext)
w.Write(bg)
} else {
utils.Error("GetBackground: Method not allowed - " + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
return
}
}

View file

@ -11,7 +11,7 @@ func Init() {
// if date is > 1st of January 2024
timeNow := time.Now()
if timeNow.Year() > 2024 || (timeNow.Year() == 2024 && timeNow.Month() > 3) {
if timeNow.Year() > 2024 || (timeNow.Year() == 2024 && timeNow.Month() > 1) {
utils.Error("Constellation: this preview version has expired, please update to use the lastest version of Constellation.", nil)
// disable constellation
configFile := utils.ReadConfigFromFile()

View file

@ -153,19 +153,6 @@ func Rollback(actions []DockerServiceCreateRollback , OnLog func(string)) {
// Edit Container
_, err := EditContainer(action.Name, action.Was, false)
if err != nil {
utils.Error("Rollback: Container", err)
OnLog(utils.DoErr("Rollback: Container %s", err))
} else {
utils.Log(fmt.Sprintf("Rolled back container %s", action.Name))
OnLog(fmt.Sprintf("Rolled back container %s\n", action.Name))
}
} else if action.Action == "restore" {
utils.Log(fmt.Sprintf("Restoring container %s...", action.Name))
// Edit Container
_, err := EditContainer("", action.Was, true)
if err != nil {
utils.Error("Rollback: Container", err)
OnLog(utils.DoErr("Rollback: Container %s", err))
@ -721,40 +708,15 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
// check if container exist
existingContainer, err := DockerClient.ContainerInspect(DockerContext, container.Name)
if err == nil {
// Edit Container
oldConfig := doctype.ContainerJSON{}
oldConfig.ContainerJSONBase = new(doctype.ContainerJSONBase)
oldConfig.Config = existingContainer.Config
oldConfig.HostConfig = existingContainer.HostConfig
oldConfig.Name = existingContainer.Name
oldConfig.NetworkSettings = existingContainer.NetworkSettings
if err == nil {
utils.Warn("CreateService: Container " + container.Name + " already exist, overwriting.")
OnLog(utils.DoWarn("Container " + container.Name + " already exist, overwriting.\n"))
// stop the container
utils.Log("CreateService: Stopping container: " + container.Name)
OnLog("Stopping container: " + container.Name + "\n")
err = DockerClient.ContainerStop(DockerContext, container.Name, conttype.StopOptions{})
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
// remove the container
utils.Log("CreateService: Removing container: " + container.Name)
OnLog("Removing container: " + container.Name + "\n")
err = DockerClient.ContainerRemove(DockerContext, container.Name, doctype.ContainerRemoveOptions{})
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
// Edit Container
newConfig := doctype.ContainerJSON{}
newConfig.ContainerJSONBase = new(doctype.ContainerJSONBase)
newConfig.Config = containerConfig
newConfig.HostConfig = hostConfig
// check if there are persistent env var
if containerConfig.Labels["cosmos-persistent-env"] != "" {
@ -779,42 +741,53 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
// if it exist, copy value to new container
if exists {
wasReplace := false
for i, newEnvVar := range containerConfig.Env {
for i, newEnvVar := range newConfig.Config.Env {
if strings.HasPrefix(newEnvVar, envVar + "=") {
containerConfig.Env[i] = envVar + "=" + strings.TrimPrefix(existingEnvVarValue, envVar + "=")
newConfig.Config.Env[i] = envVar + "=" + strings.TrimPrefix(existingEnvVarValue, envVar + "=")
wasReplace = true
break
}
}
if !wasReplace {
containerConfig.Env = append(containerConfig.Env, envVar + "=" + strings.TrimPrefix(existingEnvVarValue, envVar + "="))
newConfig.Config.Env = append(newConfig.Config.Env, envVar + "=" + strings.TrimPrefix(existingEnvVarValue, envVar + "="))
}
}
}
}
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "restore",
Type: "container",
Was: oldConfig,
})
}
_, err = DockerClient.ContainerCreate(DockerContext, containerConfig, hostConfig, networkingConfig, nil, container.Name)
_, errEdit := EditContainer(container.Name, newConfig, false)
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "remove",
Type: "container",
Name: container.Name,
})
if errEdit != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
// rollback action
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "revert",
Type: "container",
Name: container.Name,
Was: existingContainer,
})
} else {
_, err = DockerClient.ContainerCreate(DockerContext, containerConfig, hostConfig, networkingConfig, nil, container.Name)
if err != nil {
utils.Error("CreateService: Rolling back changes because of -- Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container creation error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
rollbackActions = append(rollbackActions, DockerServiceCreateRollback{
Action: "remove",
Type: "container",
Name: container.Name,
})
}
// connect to networks
for netName, netConfig := range container.Networks {
@ -907,7 +880,7 @@ func CreateService(serviceRequest DockerServiceCreateRequest, OnLog func(string)
err = DockerClient.ContainerStart(DockerContext, container.Name, doctype.ContainerStartOptions{})
if err != nil {
utils.Error("CreateService: Start Container", err)
OnLog(utils.DoErr("Rolling back changes because of -- Container start error" + container.Name + " : "+err.Error()))
OnLog(utils.DoErr("Rolling back changes because of -- Container start error: "+err.Error()))
Rollback(rollbackActions, OnLog)
return err
}
@ -1036,7 +1009,6 @@ func ReOrderServices(serviceMap map[string]ContainerCreateRequestContainer) ([]C
// If all dependencies are started, we can add this service to startOrder
if allDependenciesStarted {
utils.Debug(fmt.Sprintf("ReOrderServices: adding: %s", name))
startOrder = append(startOrder, service)
delete(serviceMap, name)
changed = true

View file

@ -5,7 +5,6 @@ import (
"net/http"
"os"
"fmt"
"strings"
"github.com/azukaar/cosmos-server/src/utils"
containerType "github.com/docker/docker/api/types/container"
@ -16,10 +15,8 @@ import (
type ContainerForm struct {
Image string `json:"image"`
User string `json:"user"`
RestartPolicy string `json:"restartPolicy"`
Env []string `json:"envVars"`
Devices []string `json:"devices"`
Labels map[string]string `json:"labels"`
PortBindings nat.PortMap `json:"portBindings"`
Volumes []mount.Mount `json:"Volumes"`
@ -72,36 +69,14 @@ func UpdateContainerRoute(w http.ResponseWriter, req *http.Request) {
container.Config.Image = form.Image
}
if(form.RestartPolicy != "") {
// THIS IS HACK BECAUSE USER IS NULLABLE, BETTER SOLUTION TO COME
container.Config.User = form.User
container.HostConfig.RestartPolicy = containerType.RestartPolicy{Name: form.RestartPolicy}
}
if(form.Env != nil) {
container.Config.Env = form.Env
}
if(form.Devices != nil) {
container.HostConfig.Devices = []containerType.DeviceMapping{}
for _, device := range form.Devices {
deviceHost := strings.Split(device, ":")[0]
deviceCont := deviceHost
if strings.Contains(device, ":") {
deviceCont = strings.Split(device, ":")[1]
}
container.HostConfig.Devices = append(container.HostConfig.Devices, containerType.DeviceMapping{
PathOnHost: deviceHost,
PathInContainer: deviceCont,
CgroupPermissions: "rwm",
})
}
}
if(form.Labels != nil) {
container.Config.Labels = form.Labels
}
if(form.PortBindings != nil) {
utils.Debug(fmt.Sprintf("UpdateContainer: PortBindings: %v", form.PortBindings))
container.HostConfig.PortBindings = form.PortBindings

View file

@ -72,12 +72,12 @@ func CheckPorts() error {
finalPorts := []string{}
for containerPort, hostConfig := range inspect.NetworkSettings.Ports {
utils.Debug("Container port: " + containerPort.Port() + "/" + containerPort.Proto())
utils.Debug("Container port: " + containerPort.Port())
for _, hostPort := range hostConfig {
utils.Debug("Host port: " + hostPort.HostPort)
ports[hostPort.HostPort] = struct{}{}
finalPorts = append(finalPorts, hostPort.HostPort + ":" + containerPort.Port() + "/" + containerPort.Proto())
finalPorts = append(finalPorts, hostPort.HostPort + ":" + containerPort.Port())
}
}

View file

@ -144,8 +144,6 @@ func EditContainer(oldContainerID string, newConfig types.ContainerJSON, noLock
oldContainer := newConfig
if(oldContainerID != "") {
utils.Log("EditContainer - inspecting previous container " + oldContainerID)
// create missing folders
for _, newmount := range newConfig.HostConfig.Mounts {

View file

@ -7,8 +7,6 @@ import (
"strconv"
"strings"
"bytes"
"gopkg.in/yaml.v2"
"os"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/docker/docker/api/types"
@ -20,17 +18,12 @@ import (
var ExportError = ""
func ExportDocker() {
config := utils.GetMainConfig()
if config.NewInstall {
return
}
ExportError = ""
errD := Connect()
if errD != nil {
ExportError = "Export Docker - cannot connect - " + errD.Error()
utils.MajorError("ExportDocker - connect - ", errD)
utils.Error("ExportDocker - connect - ", errD)
return
}
@ -39,7 +32,7 @@ func ExportDocker() {
// List containers
containers, err := DockerClient.ContainerList(DockerContext, types.ContainerListOptions{})
if err != nil {
utils.MajorError("ExportDocker - Cannot list containers", err)
utils.Error("ExportDocker - Cannot list containers", err)
ExportError = "Export Docker - Cannot list containers - " + err.Error()
return
}
@ -51,7 +44,7 @@ func ExportDocker() {
// Fetch detailed info of each container
detailedInfo, err := DockerClient.ContainerInspect(DockerContext, container.ID)
if err != nil {
utils.MajorError("Export Docker - Cannot inspect container" + container.Names[0], err)
utils.Error("Export Docker - Cannot inspect container" + container.Names[0], err)
ExportError = "Export Docker - Cannot inspect container" + container.Names[0] + " - " + err.Error()
return
}
@ -125,11 +118,11 @@ func ExportDocker() {
// Networks
Networks: func() map[string]ContainerCreateRequestServiceNetwork {
networks := make(map[string]ContainerCreateRequestServiceNetwork)
for netName, _ := range detailedInfo.NetworkSettings.Networks {
for netName, netConfig := range detailedInfo.NetworkSettings.Networks {
networks[netName] = ContainerCreateRequestServiceNetwork{
// Aliases: netConfig.Aliases,
// IPV4Address: netConfig.IPAddress,
// IPV6Address: netConfig.GlobalIPv6Address,
Aliases: netConfig.Aliases,
IPV4Address: netConfig.IPAddress,
IPV6Address: netConfig.GlobalIPv6Address,
}
}
return networks
@ -182,7 +175,7 @@ func ExportDocker() {
// List networks
networks, err := DockerClient.NetworkList(DockerContext, types.NetworkListOptions{})
if err != nil {
utils.MajorError("Export Docker - Cannot list networks", err)
utils.Error("Export Docker - Cannot list networks", err)
ExportError = "Export Docker - Cannot list networks - " + err.Error()
return
}
@ -198,7 +191,7 @@ func ExportDocker() {
// Fetch detailed info of each network
detailedInfo, err := DockerClient.NetworkInspect(DockerContext, network.ID, types.NetworkInspectOptions{})
if err != nil {
utils.MajorError("Export Docker - Cannot inspect network", err)
utils.Error("Export Docker - Cannot inspect network", err)
ExportError = "Export Docker - Cannot inspect network - " + err.Error()
return
}
@ -224,47 +217,6 @@ func ExportDocker() {
finalBackup.Networks[detailedInfo.Name] = network
}
// remove cosmos from services
if os.Getenv("HOSTNAME") != "" {
cosmos := services[os.Getenv("HOSTNAME")]
delete(services, os.Getenv("HOSTNAME"))
// export separately cosmos
// Create a buffer to hold the JSON output
var buf bytes.Buffer
// Create a new yaml encoder that writes to the buffer
encoder := yaml.NewEncoder(&buf)
// Set escape HTML to false to avoid escaping special characters
// encoder.SetEscapeHTML(false)
//format
// encoder.SetIndent("", " ")
// Use the encoder to write the structured data to the buffer
toExport := map[string]map[string]ContainerCreateRequestContainer {
"services": map[string]ContainerCreateRequestContainer {
os.Getenv("HOSTNAME"): cosmos,
},
}
err = encoder.Encode(toExport)
if err != nil {
utils.MajorError("Export Docker - Cannot marshal docker backup", err)
ExportError = "Export Docker - Cannot marshal docker backup - " + err.Error()
}
// The JSON data is now in buf.Bytes()
yamlData := buf.Bytes()
// Write the JSON data to a file
err = ioutil.WriteFile(utils.CONFIGFOLDER + "cosmos.docker-compose.yaml", yamlData, 0644)
if err != nil {
utils.MajorError("Export Docker - Cannot save docker backup", err)
ExportError = "Export Docker - Cannot save docker backup - " + err.Error()
}
}
// Convert the services map to your finalBackup struct
finalBackup.Services = services
@ -282,7 +234,7 @@ func ExportDocker() {
// Use the encoder to write the structured data to the buffer
err = encoder.Encode(finalBackup)
if err != nil {
utils.MajorError("Export Docker - Cannot marshal docker backup", err)
utils.Error("Export Docker - Cannot marshal docker backup", err)
ExportError = "Export Docker - Cannot marshal docker backup - " + err.Error()
}
@ -292,7 +244,7 @@ func ExportDocker() {
// Write the JSON data to a file
err = ioutil.WriteFile(utils.CONFIGFOLDER + "backup.cosmos-compose.json", jsonData, 0644)
if err != nil {
utils.MajorError("Export Docker - Cannot save docker backup", err)
utils.Error("Export Docker - Cannot save docker backup", err)
ExportError = "Export Docker - Cannot save docker backup - " + err.Error()
}
}

View file

@ -392,8 +392,8 @@ func InitServer() *mux.Router {
srapi.HandleFunc("/api/markets", market.MarketGet)
srapi.HandleFunc("/api/upload/{name}", UploadImage)
srapi.HandleFunc("/api/image/{name}", GetImage)
srapi.HandleFunc("/api/background", UploadBackground)
srapi.HandleFunc("/api/background/{ext}", GetBackground)
srapi.HandleFunc("/api/get-backup", configapi.BackupFileApiGet)

View file

@ -1,234 +0,0 @@
package main
import (
"io/ioutil"
"os"
"net/http"
"path/filepath"
"io"
"encoding/json"
"strings"
"fmt"
"github.com/gorilla/mux"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/azukaar/cosmos-server/src/docker"
)
var validExtensions = map[string]bool{
".jpg": true,
".jpeg": true,
".png": true,
".gif": true,
".bmp": true,
".svg": true,
".webp": true,
".tiff": true,
".avif": true,
}
func UploadImage(w http.ResponseWriter, req *http.Request) {
if utils.AdminOnly(w, req) != nil {
return
}
vars := mux.Vars(req)
originalName := vars["name"]
name := originalName + "-" + utils.GenerateRandomString(6)
// if name includes / or ..
if filepath.Clean(name) != name || strings.Contains(name, "/") {
utils.HTTPError(w, "Invalid file name", http.StatusBadRequest, "FILE002")
return
}
if(req.Method == "POST") {
// if the uploads directory does not exist, create it
if _, err := os.Stat(utils.CONFIGFOLDER + "/uploads"); os.IsNotExist(err) {
os.Mkdir(utils.CONFIGFOLDER + "/uploads", 0750)
}
// parse the form data
err := req.ParseMultipartForm(1 << 20)
if err != nil {
utils.HTTPError(w, "Error parsing form data", http.StatusInternalServerError, "FORM001")
return
}
// retrieve the file part of the form
file, header, err := req.FormFile("image")
if err != nil {
utils.HTTPError(w, "Error retrieving file from form data", http.StatusInternalServerError, "FORM002")
return
}
defer file.Close()
// get the file extension
ext := filepath.Ext(header.Filename)
if !validExtensions[ext] {
utils.HTTPError(w, "Invalid file extension " + ext, http.StatusBadRequest, "FILE001")
return
}
// create a new file in the config directory
dst, err := os.Create(utils.CONFIGFOLDER + "/uploads/" + name + ext)
if err != nil {
utils.HTTPError(w, "Error creating destination file", http.StatusInternalServerError, "FILE004")
return
}
defer dst.Close()
// copy the uploaded file to the destination file
if _, err := io.Copy(dst, file); err != nil {
utils.HTTPError(w, "Error writing to destination file", http.StatusInternalServerError, "FILE005")
return
}
// return a response to the client
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "OK",
"data": map[string]interface{}{
"path": "/cosmos/api/image/" + name + ext,
"filename": header.Filename,
"size": header.Size,
"extension": ext,
},
})
} else {
utils.Error("UploadBackground: Method not allowed - " + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
return
}
}
func GetImage(w http.ResponseWriter, req *http.Request) {
if utils.LoggedInOnly(w, req) != nil {
return
}
utils.Log("API: GetImage")
vars := mux.Vars(req)
name := vars["name"]
// if name includes / or ..
if filepath.Clean(name) != name || strings.Contains(name, "/") {
utils.Error("GetBackground: Invalid file name - " + name, nil)
utils.HTTPError(w, "Invalid file name", http.StatusBadRequest, "FILE002")
return
}
// get the file extension
ext := filepath.Ext(name)
if !validExtensions[ext] {
utils.Error("GetBackground: Invalid file extension - " + ext, nil)
utils.HTTPError(w, "Invalid file extension", http.StatusBadRequest, "FILE001")
return
}
if(req.Method == "GET") {
// get the background image
bg, err := ioutil.ReadFile(utils.CONFIGFOLDER + "/uploads/" + name)
if err != nil {
utils.Error("GetBackground: Error reading image - " + name, err)
utils.HTTPError(w, "Error reading image", http.StatusInternalServerError, "FILE003")
return
}
// return a response to the client
w.Header().Set("Content-Type", "image/" + ext)
w.Write(bg)
} else {
utils.Error("GetBackground: Method not allowed - " + req.Method, nil)
utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001")
return
}
}
func imageCleanUp() {
utils.Log("Image cleanup")
config := utils.GetMainConfig()
images := map[string]bool{}
images[config.HomepageConfig.Background] = true
for _, route := range config.HTTPConfig.ProxyConfig.Routes {
if(route.Icon != "") {
images[route.Icon] = true
}
}
// get containers
containers, err := docker.ListContainers()
if err != nil {
utils.Error("Image cleanup: Error getting containers", err)
return
}
for _, container := range containers {
if(container.Labels["cosmos-icon"] != "") {
images[container.Labels["cosmos-icon"]] = true
}
}
fmt.Println(images)
// if the uploads directory does not exist, return
if _, err := os.Stat(utils.CONFIGFOLDER + "/uploads"); os.IsNotExist(err) {
return
}
// get the files in the uploads directory
files, err := ioutil.ReadDir(utils.CONFIGFOLDER + "/uploads")
if err != nil {
utils.Error("Image cleanup: Error reading directory", err)
return
}
// loop through the files
base := "/cosmos/api/image/"
for _, f := range files {
if(!images[base + f.Name()]) {
err := os.Remove(utils.CONFIGFOLDER + "/uploads/" + f.Name())
if err != nil {
utils.Error("Image cleanup: Error removing file", err)
}
}
}
}
func MigratePre013() {
// read background from config.json
config := utils.ReadConfigFromFile()
bg := config.HomepageConfig.Background
utils.Debug("MigratePre013: background is" + bg)
// if the background start with /cosmos/api/background/
if(strings.HasPrefix(bg, "/cosmos/api/background/")) {
// get the background name
ext := strings.TrimPrefix(bg, "/cosmos/api/background/")
bgPath := utils.CONFIGFOLDER + "/background." + ext
utils.Debug("MigratePre013: background path is" + bgPath)
// if the background file exists
if _, err := os.Stat(bgPath); err == nil {
// move the background file to uploads
err := os.Rename(bgPath, utils.CONFIGFOLDER + "/uploads/background." + ext)
if err != nil {
utils.Error("MigratePre013: Error moving background file", err)
}
// update the background path
config.HomepageConfig.Background = "/cosmos/api/image/background." + ext
utils.SetBaseMainConfig(config)
}
}
}

View file

@ -45,31 +45,25 @@ func main() {
utils.Log("Docker API version: " + version.APIVersion)
}
config := utils.GetMainConfig()
if !config.NewInstall {
MigratePre013()
utils.Log("Starting monitoring services...")
utils.Log("Starting monitoring services...")
metrics.Init()
metrics.Init()
utils.Log("Starting market services...")
utils.Log("Starting market services...")
market.Init()
utils.Log("Starting OpenID services...")
market.Init()
utils.Log("Starting OpenID services...")
authorizationserver.Init()
authorizationserver.Init()
utils.Log("Starting constellation services...")
utils.Log("Starting constellation services...")
constellation.InitDNS()
constellation.Init()
constellation.InitDNS()
constellation.Init()
utils.Log("Starting server...")
}
utils.Log("Starting server...")
StartServer()
}

View file

@ -20,7 +20,6 @@ type DataDef struct {
Scale int
Unit string
Decumulate bool
DecumulatePos bool
Object string
}
@ -37,7 +36,6 @@ type DataPush struct {
Scale int
Unit string
Decumulate bool
DecumulatePos bool
Object string
}
@ -171,17 +169,13 @@ func PushSetMetric(key string, value int, def DataDef) {
lock <- true
defer func() { <-lock }()
if def.Decumulate || def.DecumulatePos {
if def.Decumulate {
if lastInserted[key] != 0 {
value = value - lastInserted[key]
if def.DecumulatePos && value < 0 {
value = 0
}
} else {
value = 0
}
}
if dp, ok := dataBuffer[cacheKey]; ok {

View file

@ -235,7 +235,7 @@ func GetSystemMetrics() {
Label: "Docker Network Received " + ds.Name,
SetOperation: "max",
AggloType: "sum",
DecumulatePos: true,
Decumulate: true,
Unit: "B",
Object: "container@" + ds.Name,
})
@ -245,7 +245,7 @@ func GetSystemMetrics() {
Label: "Docker Network Sent " + ds.Name,
SetOperation: "max",
AggloType: "sum",
DecumulatePos: true,
Decumulate: true,
Unit: "B",
Object: "container@" + ds.Name,
})

View file

@ -91,7 +91,7 @@ func UserRegister(w http.ResponseWriter, req *http.Request) {
"RegisterKeyExp": time.Time{},
"RegisteredAt": RegisteredAt,
"LastPasswordChangedAt": time.Now(),
"PasswordCycle": user.PasswordCycle + 1,
"PassowrdCycle": user.PasswordCycle + 1,
},
})

View file

@ -188,7 +188,6 @@ type ProxyRouteConfig struct {
RestrictToConstellation bool
OverwriteHostHeader string
WhitelistInboundIPs []string
Icon string
}
type EmailConfig struct {

View file

@ -73,84 +73,6 @@ var DefaultConfig = Config{
"https://raw.githubusercontent.com/StevenBlack/hosts/master/alternates/fakenews-only/hosts",
},
},
MonitoringAlerts: map[string]Alert{
"Anti Crypto-Miner": {
Name: "Anti Crypto-Miner",
Enabled: false,
Period: "daily",
TrackingMetric: "cosmos.system.docker.cpu.*",
LastTriggered: time.Time{},
Condition: AlertCondition {
Operator: "gt",
Value: 80,
Percent: false,
},
Actions: []AlertAction {
AlertAction {
Type: "notification",
Target: "",
},
AlertAction {
Type: "email",
Target: "",
},
AlertAction {
Type: "stop",
Target: "",
},
},
Throttled: false,
Severity: "warn",
},
"Anti Memory Leak": {
Name: "Anti Memory Leak",
Enabled: false,
Period: "daily",
TrackingMetric: "cosmos.system.docker.ram.*",
LastTriggered: time.Time{},
Condition: AlertCondition {
Operator: "gt",
Value: 80,
Percent: true,
},
Actions: []AlertAction {
{
Type: "notification",
Target: "",
},
{
Type: "email",
Target: "",
},
{
Type: "stop",
Target: "",
},
},
Throttled: false,
Severity: "warn",
},
"Disk Full Notification": {
Name: "Disk Full Notification",
Enabled: true,
Period: "latest",
TrackingMetric: "cosmos.system.disk./",
LastTriggered: time.Time{},
Condition: AlertCondition {
Operator: "gt",
Value: 95,
Percent: true,
},
Actions: []AlertAction {
{
Type: "notification",
Target: "",
},
},
Throttled: true,
Severity: "warn",
},
},
}
func FileExists(path string) bool {