mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-03-26 19:03:08 +00:00
Compare commits
5 commits
33311c51c8
...
9de222620c
| Author | SHA1 | Date | |
|---|---|---|---|
| 9de222620c | |||
| c49feb726f | |||
| 3e14fe7326 | |||
| c8e90fccbf | |||
| 3b1f8aa177 |
32 changed files with 526 additions and 415 deletions
|
|
@ -18,7 +18,7 @@
|
||||||
- MSVC toolchain for Rust
|
- MSVC toolchain for Rust
|
||||||
|
|
||||||
**Linux:**
|
**Linux:**
|
||||||
- podman-compose
|
- podman compose
|
||||||
|
|
||||||
### Quick Setup
|
### Quick Setup
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ cp compose/.env.production.example compose/.env.production
|
||||||
|
|
||||||
# 4. Deploy
|
# 4. Deploy
|
||||||
cd compose
|
cd compose
|
||||||
podman-compose --env-file .env.production -f production.yml up -d
|
podman compose --env-file .env.production -f production.yml up -d
|
||||||
|
|
||||||
# 5. Access at http://localhost:4321
|
# 5. Access at http://localhost:4321
|
||||||
```
|
```
|
||||||
|
|
@ -57,7 +57,7 @@ cp compose/.env.demo.example compose/.env.demo
|
||||||
|
|
||||||
# 2. Deploy
|
# 2. Deploy
|
||||||
cd compose
|
cd compose
|
||||||
podman-compose --env-file .env.demo -f demo.yml up -d
|
podman compose --env-file .env.demo -f demo.yml up -d
|
||||||
|
|
||||||
# 3. Access at http://localhost:4322
|
# 3. Access at http://localhost:4322
|
||||||
```
|
```
|
||||||
|
|
@ -105,8 +105,8 @@ To reset the demo to a clean state:
|
||||||
./scripts/demo-reset.sh
|
./scripts/demo-reset.sh
|
||||||
|
|
||||||
# Or manually:
|
# Or manually:
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml down -v
|
podman compose --env-file compose/.env.demo -f compose/demo.yml down -v
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
podman compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration Reference
|
## Configuration Reference
|
||||||
|
|
@ -141,7 +141,7 @@ For local development without containers:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Start only the database
|
# 1. Start only the database
|
||||||
podman-compose -f compose/dev.yml up -d
|
podman compose -f compose/dev.yml up -d
|
||||||
|
|
||||||
# 2. Configure backend environment
|
# 2. Configure backend environment
|
||||||
cp backend/.env.example backend/.env
|
cp backend/.env.example backend/.env
|
||||||
|
|
@ -159,10 +159,10 @@ npm run dev
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# View all logs
|
# View all logs
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml logs -f
|
podman compose --env-file compose/.env.demo -f compose/demo.yml logs -f
|
||||||
|
|
||||||
# View specific service
|
# View specific service
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml logs -f backend
|
podman compose --env-file compose/.env.demo -f compose/demo.yml logs -f backend
|
||||||
|
|
||||||
# Check health
|
# Check health
|
||||||
curl http://localhost:3001/health
|
curl http://localhost:3001/health
|
||||||
|
|
@ -173,10 +173,10 @@ curl http://localhost:3001/health
|
||||||
### Database connection issues
|
### Database connection issues
|
||||||
```bash
|
```bash
|
||||||
# Check if postgres is running
|
# Check if postgres is running
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml ps
|
podman compose --env-file compose/.env.demo -f compose/demo.yml ps
|
||||||
|
|
||||||
# View postgres logs
|
# View postgres logs
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml logs postgres
|
podman compose --env-file compose/.env.demo -f compose/demo.yml logs postgres
|
||||||
```
|
```
|
||||||
|
|
||||||
### Migration failures
|
### Migration failures
|
||||||
|
|
@ -194,6 +194,6 @@ SELECT * FROM _sqlx_migrations;
|
||||||
### Reset everything
|
### Reset everything
|
||||||
```bash
|
```bash
|
||||||
# Nuclear option - removes all data and volumes
|
# Nuclear option - removes all data and volumes
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml down -v
|
podman compose --env-file compose/.env.demo -f compose/demo.yml down -v
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
podman compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ Likwid implements a set of principles for **modular governance infrastructure**:
|
||||||
|
|
||||||
**Linux:**
|
**Linux:**
|
||||||
|
|
||||||
- Podman + podman-compose
|
- Podman + podman compose
|
||||||
- Rust (rustup)
|
- Rust (rustup)
|
||||||
- Node.js LTS
|
- Node.js LTS
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ sqlx migrate run --source migrations_demo
|
||||||
```bash
|
```bash
|
||||||
# Uses separate database on port 5433, backend on 3001, frontend on 4322
|
# Uses separate database on port 5433, backend on 3001, frontend on 4322
|
||||||
cp compose/.env.demo.example compose/.env.demo
|
cp compose/.env.demo.example compose/.env.demo
|
||||||
podman-compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
podman compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### For real users (Production)
|
### For real users (Production)
|
||||||
|
|
@ -76,7 +76,7 @@ cp compose/.env.production.example compose/.env.production
|
||||||
# Edit with secure passwords and your domain
|
# Edit with secure passwords and your domain
|
||||||
|
|
||||||
# 2. Deploy
|
# 2. Deploy
|
||||||
podman-compose --env-file compose/.env.production -f compose/production.yml up -d
|
podman compose --env-file compose/.env.production -f compose/production.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
## Common Tasks
|
## Common Tasks
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "SELECT id, setup_completed, instance_name, platform_mode,\n registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode\n FROM instance_settings LIMIT 1",
|
"query": "SELECT id, setup_completed, instance_name, platform_mode,\n theme_id, registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode\n FROM instance_settings LIMIT 1",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
|
@ -25,31 +25,36 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 4,
|
"ordinal": 4,
|
||||||
|
"name": "theme_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
"name": "registration_enabled",
|
"name": "registration_enabled",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 5,
|
"ordinal": 6,
|
||||||
"name": "registration_mode",
|
"name": "registration_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 6,
|
"ordinal": 7,
|
||||||
"name": "default_community_visibility",
|
"name": "default_community_visibility",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 7,
|
"ordinal": 8,
|
||||||
"name": "allow_private_communities",
|
"name": "allow_private_communities",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 8,
|
"ordinal": 9,
|
||||||
"name": "default_plugin_policy",
|
"name": "default_plugin_policy",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 9,
|
"ordinal": 10,
|
||||||
"name": "default_moderation_mode",
|
"name": "default_moderation_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
}
|
}
|
||||||
|
|
@ -67,8 +72,9 @@
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "18c0fb05da45a3eea514f660bc4ac4d6aca71442645666a9c08db8f2a564ff6c"
|
"hash": "0620f314de8df0c7990ef63fda55f2ff646d5159c59d8288e4ffdfdb07dc159f"
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "SELECT setup_completed, instance_name, platform_mode,\n registration_enabled, registration_mode,\n single_community_id\n FROM instance_settings\n LIMIT 1",
|
"query": "SELECT setup_completed, instance_name, theme_id, platform_mode,\n registration_enabled, registration_mode,\n single_community_id\n FROM instance_settings\n LIMIT 1",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
|
@ -15,21 +15,26 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 2,
|
"ordinal": 2,
|
||||||
"name": "platform_mode",
|
"name": "theme_id",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 3,
|
"ordinal": 3,
|
||||||
|
"name": "platform_mode",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 4,
|
||||||
"name": "registration_enabled",
|
"name": "registration_enabled",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 4,
|
"ordinal": 5,
|
||||||
"name": "registration_mode",
|
"name": "registration_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 5,
|
"ordinal": 6,
|
||||||
"name": "single_community_id",
|
"name": "single_community_id",
|
||||||
"type_info": "Uuid"
|
"type_info": "Uuid"
|
||||||
}
|
}
|
||||||
|
|
@ -43,8 +48,9 @@
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
true
|
true
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "200e864fa5778cf58d36d49f94a4006f7d104eb84e6f166b795df0f222ee93d8"
|
"hash": "593dc329afc129680dd505221df649aa8cb544841fa78a5fe740adfbb4439502"
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "UPDATE instance_settings SET\n setup_completed = true,\n setup_completed_at = NOW(),\n setup_completed_by = $1,\n instance_name = $2,\n platform_mode = $3,\n single_community_id = $4\n RETURNING id, setup_completed, instance_name, platform_mode,\n registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode",
|
"query": "UPDATE instance_settings SET\n setup_completed = true,\n setup_completed_at = NOW(),\n setup_completed_by = $1,\n instance_name = $2,\n platform_mode = $3,\n single_community_id = $4\n RETURNING id, setup_completed, instance_name, platform_mode,\n theme_id,\n registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
|
@ -25,31 +25,36 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 4,
|
"ordinal": 4,
|
||||||
|
"name": "theme_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
"name": "registration_enabled",
|
"name": "registration_enabled",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 5,
|
"ordinal": 6,
|
||||||
"name": "registration_mode",
|
"name": "registration_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 6,
|
"ordinal": 7,
|
||||||
"name": "default_community_visibility",
|
"name": "default_community_visibility",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 7,
|
"ordinal": 8,
|
||||||
"name": "allow_private_communities",
|
"name": "allow_private_communities",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 8,
|
"ordinal": 9,
|
||||||
"name": "default_plugin_policy",
|
"name": "default_plugin_policy",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 9,
|
"ordinal": 10,
|
||||||
"name": "default_moderation_mode",
|
"name": "default_moderation_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
}
|
}
|
||||||
|
|
@ -72,8 +77,9 @@
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "b9586185e84644f0bd936d7bf5e9bec6ebeaba77ab354d0b7096d9334656497f"
|
"hash": "8dd178663df95d64d72c776e3b8bda63851d2ad0e13a6d80b327610078ecbaeb"
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"db_name": "PostgreSQL",
|
"db_name": "PostgreSQL",
|
||||||
"query": "UPDATE instance_settings SET\n instance_name = COALESCE($1, instance_name),\n platform_mode = COALESCE($2, platform_mode),\n registration_enabled = COALESCE($3, registration_enabled),\n registration_mode = COALESCE($4, registration_mode)\n RETURNING id, setup_completed, instance_name, platform_mode,\n registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode",
|
"query": "UPDATE instance_settings SET\n instance_name = COALESCE($1, instance_name),\n theme_id = COALESCE($2, theme_id),\n platform_mode = COALESCE($3, platform_mode),\n registration_enabled = COALESCE($4, registration_enabled),\n registration_mode = COALESCE($5, registration_mode)\n RETURNING id, setup_completed, instance_name, platform_mode,\n theme_id, registration_enabled, registration_mode,\n default_community_visibility, allow_private_communities,\n default_plugin_policy, default_moderation_mode",
|
||||||
"describe": {
|
"describe": {
|
||||||
"columns": [
|
"columns": [
|
||||||
{
|
{
|
||||||
|
|
@ -25,37 +25,43 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 4,
|
"ordinal": 4,
|
||||||
|
"name": "theme_id",
|
||||||
|
"type_info": "Varchar"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ordinal": 5,
|
||||||
"name": "registration_enabled",
|
"name": "registration_enabled",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 5,
|
"ordinal": 6,
|
||||||
"name": "registration_mode",
|
"name": "registration_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 6,
|
"ordinal": 7,
|
||||||
"name": "default_community_visibility",
|
"name": "default_community_visibility",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 7,
|
"ordinal": 8,
|
||||||
"name": "allow_private_communities",
|
"name": "allow_private_communities",
|
||||||
"type_info": "Bool"
|
"type_info": "Bool"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 8,
|
"ordinal": 9,
|
||||||
"name": "default_plugin_policy",
|
"name": "default_plugin_policy",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ordinal": 9,
|
"ordinal": 10,
|
||||||
"name": "default_moderation_mode",
|
"name": "default_moderation_mode",
|
||||||
"type_info": "Varchar"
|
"type_info": "Varchar"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"parameters": {
|
"parameters": {
|
||||||
"Left": [
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
"Varchar",
|
"Varchar",
|
||||||
"Varchar",
|
"Varchar",
|
||||||
"Bool",
|
"Bool",
|
||||||
|
|
@ -72,8 +78,9 @@
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
|
false,
|
||||||
false
|
false
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"hash": "c35608b0d7569f739dda24b3da59b7b500ff26f5e79433b3f7e3625d91177d26"
|
"hash": "a903d88370faa52169ffd4ec6a54a789ee4a6173fe84aca0ef8dedaa46b1f93c"
|
||||||
}
|
}
|
||||||
2
backend/migrations/20260215190000_instance_theme.sql
Normal file
2
backend/migrations/20260215190000_instance_theme.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE instance_settings
|
||||||
|
ADD COLUMN IF NOT EXISTS theme_id VARCHAR(50) NOT NULL DEFAULT 'neutral';
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
UPDATE instance_settings
|
||||||
|
SET theme_id = 'breeze-dark'
|
||||||
|
WHERE theme_id = 'neutral';
|
||||||
|
|
@ -40,7 +40,7 @@ async fn get_demo_status(State(state): State<DemoState>) -> impl IntoResponse {
|
||||||
vec![
|
vec![
|
||||||
"Cannot delete communities",
|
"Cannot delete communities",
|
||||||
"Cannot delete users",
|
"Cannot delete users",
|
||||||
"Cannot modify instance settings",
|
"Cannot modify instance settings (except instance theme)",
|
||||||
"Data resets periodically"
|
"Data resets periodically"
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ pub struct SetupStatus {
|
||||||
pub struct PublicInstanceSettings {
|
pub struct PublicInstanceSettings {
|
||||||
pub setup_completed: bool,
|
pub setup_completed: bool,
|
||||||
pub instance_name: String,
|
pub instance_name: String,
|
||||||
|
pub theme_id: String,
|
||||||
pub platform_mode: String,
|
pub platform_mode: String,
|
||||||
pub registration_enabled: bool,
|
pub registration_enabled: bool,
|
||||||
pub registration_mode: String,
|
pub registration_mode: String,
|
||||||
|
|
@ -42,6 +43,7 @@ pub struct InstanceSettings {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub setup_completed: bool,
|
pub setup_completed: bool,
|
||||||
pub instance_name: String,
|
pub instance_name: String,
|
||||||
|
pub theme_id: String,
|
||||||
pub platform_mode: String,
|
pub platform_mode: String,
|
||||||
pub registration_enabled: bool,
|
pub registration_enabled: bool,
|
||||||
pub registration_mode: String,
|
pub registration_mode: String,
|
||||||
|
|
@ -64,6 +66,8 @@ pub struct UpdateInstanceRequest {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub instance_name: Option<String>,
|
pub instance_name: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
pub theme_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
pub platform_mode: Option<String>,
|
pub platform_mode: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub registration_enabled: Option<bool>,
|
pub registration_enabled: Option<bool>,
|
||||||
|
|
@ -71,6 +75,32 @@ pub struct UpdateInstanceRequest {
|
||||||
pub registration_mode: Option<String>,
|
pub registration_mode: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KNOWN_THEME_IDS: [&str; 4] = ["neutral", "breeze-light", "breeze-dark", "opensuse"];
|
||||||
|
|
||||||
|
fn validate_theme_id(theme_id: &str) -> Result<(), (StatusCode, String)> {
|
||||||
|
if theme_id.trim().is_empty() {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Theme cannot be empty".to_string()));
|
||||||
|
}
|
||||||
|
if theme_id.len() > 50 {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Theme is too long".to_string()));
|
||||||
|
}
|
||||||
|
if !theme_id
|
||||||
|
.chars()
|
||||||
|
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
|
||||||
|
{
|
||||||
|
return Err((
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Theme contains invalid characters".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !KNOWN_THEME_IDS.iter().any(|t| t == &theme_id) {
|
||||||
|
return Err((StatusCode::BAD_REQUEST, "Unknown theme".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
#[derive(Debug, Serialize)]
|
||||||
pub struct CommunitySettings {
|
pub struct CommunitySettings {
|
||||||
pub community_id: Uuid,
|
pub community_id: Uuid,
|
||||||
|
|
@ -121,7 +151,7 @@ async fn get_public_settings(
|
||||||
State(pool): State<PgPool>,
|
State(pool): State<PgPool>,
|
||||||
) -> Result<Json<PublicInstanceSettings>, String> {
|
) -> Result<Json<PublicInstanceSettings>, String> {
|
||||||
let row = sqlx::query!(
|
let row = sqlx::query!(
|
||||||
r#"SELECT setup_completed, instance_name, platform_mode,
|
r#"SELECT setup_completed, instance_name, theme_id, platform_mode,
|
||||||
registration_enabled, registration_mode,
|
registration_enabled, registration_mode,
|
||||||
single_community_id
|
single_community_id
|
||||||
FROM instance_settings
|
FROM instance_settings
|
||||||
|
|
@ -135,6 +165,7 @@ async fn get_public_settings(
|
||||||
return Ok(Json(PublicInstanceSettings {
|
return Ok(Json(PublicInstanceSettings {
|
||||||
setup_completed: false,
|
setup_completed: false,
|
||||||
instance_name: "Likwid".to_string(),
|
instance_name: "Likwid".to_string(),
|
||||||
|
theme_id: "neutral".to_string(),
|
||||||
platform_mode: "open".to_string(),
|
platform_mode: "open".to_string(),
|
||||||
registration_enabled: true,
|
registration_enabled: true,
|
||||||
registration_mode: "open".to_string(),
|
registration_mode: "open".to_string(),
|
||||||
|
|
@ -161,6 +192,7 @@ async fn get_public_settings(
|
||||||
Ok(Json(PublicInstanceSettings {
|
Ok(Json(PublicInstanceSettings {
|
||||||
setup_completed: r.setup_completed,
|
setup_completed: r.setup_completed,
|
||||||
instance_name: r.instance_name,
|
instance_name: r.instance_name,
|
||||||
|
theme_id: r.theme_id,
|
||||||
platform_mode: r.platform_mode,
|
platform_mode: r.platform_mode,
|
||||||
registration_enabled: r.registration_enabled,
|
registration_enabled: r.registration_enabled,
|
||||||
registration_mode: r.registration_mode,
|
registration_mode: r.registration_mode,
|
||||||
|
|
@ -224,6 +256,7 @@ async fn complete_setup(
|
||||||
platform_mode = $3,
|
platform_mode = $3,
|
||||||
single_community_id = $4
|
single_community_id = $4
|
||||||
RETURNING id, setup_completed, instance_name, platform_mode,
|
RETURNING id, setup_completed, instance_name, platform_mode,
|
||||||
|
theme_id,
|
||||||
registration_enabled, registration_mode,
|
registration_enabled, registration_mode,
|
||||||
default_community_visibility, allow_private_communities,
|
default_community_visibility, allow_private_communities,
|
||||||
default_plugin_policy, default_moderation_mode"#,
|
default_plugin_policy, default_moderation_mode"#,
|
||||||
|
|
@ -240,6 +273,7 @@ async fn complete_setup(
|
||||||
id: settings.id,
|
id: settings.id,
|
||||||
setup_completed: settings.setup_completed,
|
setup_completed: settings.setup_completed,
|
||||||
instance_name: settings.instance_name,
|
instance_name: settings.instance_name,
|
||||||
|
theme_id: settings.theme_id,
|
||||||
platform_mode: settings.platform_mode,
|
platform_mode: settings.platform_mode,
|
||||||
registration_enabled: settings.registration_enabled,
|
registration_enabled: settings.registration_enabled,
|
||||||
registration_mode: settings.registration_mode,
|
registration_mode: settings.registration_mode,
|
||||||
|
|
@ -260,7 +294,7 @@ async fn get_instance_settings(
|
||||||
|
|
||||||
let s = sqlx::query!(
|
let s = sqlx::query!(
|
||||||
r#"SELECT id, setup_completed, instance_name, platform_mode,
|
r#"SELECT id, setup_completed, instance_name, platform_mode,
|
||||||
registration_enabled, registration_mode,
|
theme_id, registration_enabled, registration_mode,
|
||||||
default_community_visibility, allow_private_communities,
|
default_community_visibility, allow_private_communities,
|
||||||
default_plugin_policy, default_moderation_mode
|
default_plugin_policy, default_moderation_mode
|
||||||
FROM instance_settings LIMIT 1"#
|
FROM instance_settings LIMIT 1"#
|
||||||
|
|
@ -273,6 +307,7 @@ async fn get_instance_settings(
|
||||||
id: s.id,
|
id: s.id,
|
||||||
setup_completed: s.setup_completed,
|
setup_completed: s.setup_completed,
|
||||||
instance_name: s.instance_name,
|
instance_name: s.instance_name,
|
||||||
|
theme_id: s.theme_id,
|
||||||
platform_mode: s.platform_mode,
|
platform_mode: s.platform_mode,
|
||||||
registration_enabled: s.registration_enabled,
|
registration_enabled: s.registration_enabled,
|
||||||
registration_mode: s.registration_mode,
|
registration_mode: s.registration_mode,
|
||||||
|
|
@ -293,24 +328,37 @@ async fn update_instance_settings(
|
||||||
// Check platform settings permission
|
// Check platform settings permission
|
||||||
require_permission(&pool, auth.user_id, perms::PLATFORM_SETTINGS, None).await?;
|
require_permission(&pool, auth.user_id, perms::PLATFORM_SETTINGS, None).await?;
|
||||||
|
|
||||||
|
if let Some(theme_id) = req.theme_id.as_deref() {
|
||||||
|
validate_theme_id(theme_id)?;
|
||||||
|
}
|
||||||
|
|
||||||
if config.is_demo() {
|
if config.is_demo() {
|
||||||
return Err((
|
let allowed = req.theme_id.is_some()
|
||||||
StatusCode::FORBIDDEN,
|
&& req.instance_name.is_none()
|
||||||
"Instance settings cannot be modified in demo mode".to_string(),
|
&& req.platform_mode.is_none()
|
||||||
));
|
&& req.registration_enabled.is_none()
|
||||||
|
&& req.registration_mode.is_none();
|
||||||
|
if !allowed {
|
||||||
|
return Err((
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
"Only theme updates are allowed in demo mode".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let s = sqlx::query!(
|
let s = sqlx::query!(
|
||||||
r#"UPDATE instance_settings SET
|
r#"UPDATE instance_settings SET
|
||||||
instance_name = COALESCE($1, instance_name),
|
instance_name = COALESCE($1, instance_name),
|
||||||
platform_mode = COALESCE($2, platform_mode),
|
theme_id = COALESCE($2, theme_id),
|
||||||
registration_enabled = COALESCE($3, registration_enabled),
|
platform_mode = COALESCE($3, platform_mode),
|
||||||
registration_mode = COALESCE($4, registration_mode)
|
registration_enabled = COALESCE($4, registration_enabled),
|
||||||
|
registration_mode = COALESCE($5, registration_mode)
|
||||||
RETURNING id, setup_completed, instance_name, platform_mode,
|
RETURNING id, setup_completed, instance_name, platform_mode,
|
||||||
registration_enabled, registration_mode,
|
theme_id, registration_enabled, registration_mode,
|
||||||
default_community_visibility, allow_private_communities,
|
default_community_visibility, allow_private_communities,
|
||||||
default_plugin_policy, default_moderation_mode"#,
|
default_plugin_policy, default_moderation_mode"#,
|
||||||
req.instance_name,
|
req.instance_name,
|
||||||
|
req.theme_id,
|
||||||
req.platform_mode,
|
req.platform_mode,
|
||||||
req.registration_enabled,
|
req.registration_enabled,
|
||||||
req.registration_mode
|
req.registration_mode
|
||||||
|
|
@ -323,6 +371,7 @@ async fn update_instance_settings(
|
||||||
id: s.id,
|
id: s.id,
|
||||||
setup_completed: s.setup_completed,
|
setup_completed: s.setup_completed,
|
||||||
instance_name: s.instance_name,
|
instance_name: s.instance_name,
|
||||||
|
theme_id: s.theme_id,
|
||||||
platform_mode: s.platform_mode,
|
platform_mode: s.platform_mode,
|
||||||
registration_enabled: s.registration_enabled,
|
registration_enabled: s.registration_enabled,
|
||||||
registration_mode: s.registration_mode,
|
registration_mode: s.registration_mode,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Demo deployment - includes demo users, seed data, and restricted actions
|
# Demo deployment - includes demo users, seed data, and restricted actions
|
||||||
# Usage: podman-compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
# Usage: podman compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
||||||
# Reset: podman-compose --env-file compose/.env.demo -f compose/demo.yml down -v; podman-compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
# Reset: podman compose --env-file compose/.env.demo -f compose/demo.yml down -v; podman compose --env-file compose/.env.demo -f compose/demo.yml up -d
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# Production deployment - clean instance without demo data
|
# Production deployment - clean instance without demo data
|
||||||
# Usage: podman-compose -f compose/production.yml up -d
|
# Usage: podman compose -f compose/production.yml up -d
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
postgres:
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ find "$BACKUP_DIR" -name "*.dump.gz" -mtime +$RETENTION_DAYS -delete
|
||||||
### Containerized Backup
|
### Containerized Backup
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# If using podman-compose
|
# If using podman compose
|
||||||
podman exec likwid-prod-db pg_dump -U likwid likwid_prod > backup.sql
|
podman exec likwid-prod-db pg_dump -U likwid likwid_prod > backup.sql
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ Managed via the Admin panel or API:
|
||||||
- **Instance Name** - Display name for your Likwid instance
|
- **Instance Name** - Display name for your Likwid instance
|
||||||
- **Instance Description** - Brief description
|
- **Instance Description** - Brief description
|
||||||
- **Registration** - Open, invite-only, or closed
|
- **Registration** - Open, invite-only, or closed
|
||||||
- **Email Verification** - Required or optional
|
- **Approval workflows** - Registration and community creation can be open, invite-only, or require admin approval
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ Required settings:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd compose
|
cd compose
|
||||||
podman-compose --env-file .env.production -f production.yml up -d
|
podman compose --env-file .env.production -f production.yml up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4. Access
|
### 4. Access
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Likwid is a modular governance platform for distributed organizations. This guid
|
||||||
- Email address
|
- Email address
|
||||||
- Display name (shown to others)
|
- Display name (shown to others)
|
||||||
- Password
|
- Password
|
||||||
4. Verify your email if required by the instance
|
4. If registration requires approval, your account will be pending until an admin approves it
|
||||||
|
|
||||||
## Exploring Without an Account
|
## Exploring Without an Account
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
import { DEFAULT_THEME, themes as themeRegistry } from '../lib/themes';
|
import { DEFAULT_THEME, themes as themeRegistry } from '../lib/themes';
|
||||||
import { API_BASE as apiBase } from '../lib/api';
|
import { API_BASE as apiBase, SERVER_API_BASE } from '../lib/api';
|
||||||
import VotingIcons from '../components/icons/VotingIcons.astro';
|
import VotingIcons from '../components/icons/VotingIcons.astro';
|
||||||
import DesignSystemStyles from '../components/ui/DesignSystemStyles.astro';
|
import DesignSystemStyles from '../components/ui/DesignSystemStyles.astro';
|
||||||
|
|
||||||
|
|
@ -22,11 +22,24 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
||||||
Object.entries(themeRegistry).map(([id, t]) => [id, { isDark: t.isDark, colors: t.colors }]),
|
Object.entries(themeRegistry).map(([id, t]) => [id, { isDark: t.isDark, colors: t.colors }]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultTheme = DEFAULT_THEME;
|
const settingsApiBase = SERVER_API_BASE || 'http://127.0.0.1:3000';
|
||||||
|
|
||||||
|
let defaultTheme = DEFAULT_THEME;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${settingsApiBase}/api/settings/public`);
|
||||||
|
if (res.ok) {
|
||||||
|
const settings = await res.json();
|
||||||
|
if (settings && typeof settings.theme_id === 'string' && themeRegistry[settings.theme_id]) {
|
||||||
|
defaultTheme = settings.theme_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_e) {}
|
||||||
|
|
||||||
|
const initialTheme = themeRegistry[defaultTheme] || themeRegistry[DEFAULT_THEME];
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="neutral" data-theme-mode="dark">
|
<html lang="en" data-theme={defaultTheme} data-theme-mode={initialTheme.isDark ? 'dark' : 'light'}>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
@ -36,9 +49,9 @@ const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_S
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<title>{title} | Likwid</title>
|
<title>{title} | Likwid</title>
|
||||||
<script is:inline define:vars={{ themes, defaultTheme }}>
|
<script is:inline define:vars={{ themes, defaultTheme, publicDemoSite }}>
|
||||||
(function() {
|
(function() {
|
||||||
const saved = localStorage.getItem('likwid-theme') || defaultTheme;
|
const saved = publicDemoSite ? defaultTheme : (localStorage.getItem('likwid-theme') || defaultTheme);
|
||||||
const theme = themes[saved] || themes[defaultTheme];
|
const theme = themes[saved] || themes[defaultTheme];
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const c = theme.colors;
|
const c = theme.colors;
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ interface Props {
|
||||||
}
|
}
|
||||||
|
|
||||||
import { DEFAULT_THEME, themes as themeRegistry } from '../lib/themes';
|
import { DEFAULT_THEME, themes as themeRegistry } from '../lib/themes';
|
||||||
|
import { SERVER_API_BASE } from '../lib/api';
|
||||||
import DesignSystemStyles from '../components/ui/DesignSystemStyles.astro';
|
import DesignSystemStyles from '../components/ui/DesignSystemStyles.astro';
|
||||||
|
|
||||||
function isEnabled(v: string | undefined): boolean {
|
function isEnabled(v: string | undefined): boolean {
|
||||||
|
|
@ -21,11 +22,24 @@ const themes = Object.fromEntries(
|
||||||
Object.entries(themeRegistry).map(([id, t]) => [id, { isDark: t.isDark, colors: t.colors }]),
|
Object.entries(themeRegistry).map(([id, t]) => [id, { isDark: t.isDark, colors: t.colors }]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultTheme = DEFAULT_THEME;
|
const settingsApiBase = SERVER_API_BASE || 'http://127.0.0.1:3000';
|
||||||
|
|
||||||
|
let defaultTheme = DEFAULT_THEME;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${settingsApiBase}/api/settings/public`);
|
||||||
|
if (res.ok) {
|
||||||
|
const settings = await res.json();
|
||||||
|
if (settings && typeof settings.theme_id === 'string' && themeRegistry[settings.theme_id]) {
|
||||||
|
defaultTheme = settings.theme_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_e) {}
|
||||||
|
|
||||||
|
const initialTheme = themeRegistry[defaultTheme] || themeRegistry[DEFAULT_THEME];
|
||||||
---
|
---
|
||||||
|
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en" data-theme="neutral" data-theme-mode="dark">
|
<html lang="en" data-theme={defaultTheme} data-theme-mode={initialTheme.isDark ? 'dark' : 'light'}>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
@ -35,9 +49,9 @@ const defaultTheme = DEFAULT_THEME;
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
|
||||||
<title>{title} | Likwid</title>
|
<title>{title} | Likwid</title>
|
||||||
<script is:inline define:vars={{ themes, defaultTheme }}>
|
<script is:inline define:vars={{ themes, defaultTheme, publicDemoSite }}>
|
||||||
(function() {
|
(function() {
|
||||||
const saved = localStorage.getItem('likwid-theme') || defaultTheme;
|
const saved = publicDemoSite ? defaultTheme : (localStorage.getItem('likwid-theme') || defaultTheme);
|
||||||
const theme = themes[saved] || themes[defaultTheme];
|
const theme = themes[saved] || themes[defaultTheme];
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
const c = theme.colors;
|
const c = theme.colors;
|
||||||
|
|
@ -103,11 +117,6 @@ const defaultTheme = DEFAULT_THEME;
|
||||||
<a href="/docs">Documentation</a>
|
<a href="/docs">Documentation</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="nav-actions">
|
<div class="nav-actions">
|
||||||
<select id="theme-select" class="theme-select" aria-label="Theme">
|
|
||||||
{Object.values(themeRegistry).map((t) => (
|
|
||||||
<option value={t.id}>{t.name}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
<a href="/demo" class="ui-btn ui-btn-primary">Explore Demo</a>
|
<a href="/demo" class="ui-btn ui-btn-primary">Explore Demo</a>
|
||||||
{!publicDemoSite ? <a href="/login" class="ui-btn ui-btn-secondary">Sign In</a> : null}
|
{!publicDemoSite ? <a href="/login" class="ui-btn ui-btn-secondary">Sign In</a> : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -154,10 +163,9 @@ const defaultTheme = DEFAULT_THEME;
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
<script is:inline define:vars={{ themes, defaultTheme }}>
|
<script is:inline define:vars={{ themes, defaultTheme, publicDemoSite }}>
|
||||||
const nav = document.getElementById('public-nav');
|
const nav = document.getElementById('public-nav');
|
||||||
const toggle = document.getElementById('public-nav-toggle');
|
const toggle = document.getElementById('public-nav-toggle');
|
||||||
const themeSelect = document.getElementById('theme-select');
|
|
||||||
|
|
||||||
function openNav() {
|
function openNav() {
|
||||||
if (!nav || !toggle) return;
|
if (!nav || !toggle) return;
|
||||||
|
|
@ -239,58 +247,6 @@ const defaultTheme = DEFAULT_THEME;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySelectedTheme() {
|
|
||||||
if (!(themeSelect instanceof HTMLSelectElement)) return;
|
|
||||||
const selected = themeSelect.value || defaultTheme;
|
|
||||||
const theme = themes[selected] || themes[defaultTheme];
|
|
||||||
const root = document.documentElement;
|
|
||||||
const c = theme.colors;
|
|
||||||
root.style.setProperty('--color-bg', c.bg);
|
|
||||||
root.style.setProperty('--color-bg-alt', c.bgAlt);
|
|
||||||
root.style.setProperty('--color-surface', c.surface);
|
|
||||||
root.style.setProperty('--color-surface-hover', c.surfaceHover);
|
|
||||||
root.style.setProperty('--color-border', c.border);
|
|
||||||
root.style.setProperty('--color-border-hover', c.borderHover);
|
|
||||||
root.style.setProperty('--color-text', c.text);
|
|
||||||
root.style.setProperty('--color-text-muted', c.textMuted);
|
|
||||||
root.style.setProperty('--color-text-inverse', c.textInverse);
|
|
||||||
root.style.setProperty('--color-primary', c.primary);
|
|
||||||
root.style.setProperty('--color-primary-hover', c.primaryHover);
|
|
||||||
root.style.setProperty('--color-primary-muted', c.primaryMuted);
|
|
||||||
root.style.setProperty('--color-secondary', c.secondary);
|
|
||||||
root.style.setProperty('--color-secondary-hover', c.secondaryHover);
|
|
||||||
root.style.setProperty('--color-info', c.info);
|
|
||||||
root.style.setProperty('--color-info-hover', c.infoHover);
|
|
||||||
root.style.setProperty('--color-info-muted', c.infoMuted);
|
|
||||||
root.style.setProperty('--color-neutral', c.neutral);
|
|
||||||
root.style.setProperty('--color-neutral-hover', c.neutralHover);
|
|
||||||
root.style.setProperty('--color-neutral-muted', c.neutralMuted);
|
|
||||||
root.style.setProperty('--color-success', c.success);
|
|
||||||
root.style.setProperty('--color-success-muted', c.successMuted);
|
|
||||||
root.style.setProperty('--color-success-hover', c.successHover);
|
|
||||||
root.style.setProperty('--color-warning', c.warning);
|
|
||||||
root.style.setProperty('--color-warning-muted', c.warningMuted);
|
|
||||||
root.style.setProperty('--color-error', c.error);
|
|
||||||
root.style.setProperty('--color-error-muted', c.errorMuted);
|
|
||||||
root.style.setProperty('--color-error-hover', c.errorHover);
|
|
||||||
root.style.setProperty('--color-link', c.link);
|
|
||||||
root.style.setProperty('--color-link-visited', c.linkVisited);
|
|
||||||
root.style.setProperty('--color-overlay', c.overlay);
|
|
||||||
root.style.setProperty('--color-field-bg', c.fieldBg);
|
|
||||||
root.style.setProperty('--color-on-primary', c.onPrimary);
|
|
||||||
root.setAttribute('data-theme', selected);
|
|
||||||
root.setAttribute('data-theme-mode', theme.isDark ? 'dark' : 'light');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeSelect instanceof HTMLSelectElement) {
|
|
||||||
const saved = localStorage.getItem('likwid-theme') || defaultTheme;
|
|
||||||
themeSelect.value = themes[saved] ? saved : defaultTheme;
|
|
||||||
themeSelect.addEventListener('change', () => {
|
|
||||||
localStorage.setItem('likwid-theme', themeSelect.value);
|
|
||||||
applySelectedTheme();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -449,20 +405,6 @@ const defaultTheme = DEFAULT_THEME;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.theme-select {
|
|
||||||
width: auto;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
font-size: 0.875rem;
|
|
||||||
background: rgba(255, 255, 255, 0.06);
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
color: var(--color-text);
|
|
||||||
}
|
|
||||||
|
|
||||||
.theme-select:hover {
|
|
||||||
border-color: var(--color-border-hover);
|
|
||||||
}
|
|
||||||
|
|
||||||
.public-main {
|
.public-main {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ import { API_BASE as apiBase } from '../../lib/api';
|
||||||
<label for="instance_name">Platform Name</label>
|
<label for="instance_name">Platform Name</label>
|
||||||
<input type="text" id="instance_name" name="instance_name" />
|
<input type="text" id="instance_name" name="instance_name" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="theme_id">Theme</label>
|
||||||
|
<select id="theme_id" name="theme_id">
|
||||||
|
<option value="neutral">Neutral Dark</option>
|
||||||
|
<option value="breeze-light">Breeze Light</option>
|
||||||
|
<option value="breeze-dark">Breeze Dark</option>
|
||||||
|
<option value="opensuse">openSUSE</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Platform Mode -->
|
<!-- Platform Mode -->
|
||||||
|
|
@ -182,6 +192,8 @@ import { API_BASE as apiBase } from '../../lib/api';
|
||||||
const saveBtn = document.getElementById('save-btn');
|
const saveBtn = document.getElementById('save-btn');
|
||||||
const saveStatus = document.getElementById('save-status');
|
const saveStatus = document.getElementById('save-status');
|
||||||
|
|
||||||
|
let initialSettings = null;
|
||||||
|
|
||||||
if (!form || !loadingEl || !errorEl || !saveStatus) return;
|
if (!form || !loadingEl || !errorEl || !saveStatus) return;
|
||||||
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
|
|
@ -213,9 +225,11 @@ import { API_BASE as apiBase } from '../../lib/api';
|
||||||
}
|
}
|
||||||
|
|
||||||
const settings = await res.json();
|
const settings = await res.json();
|
||||||
|
initialSettings = settings;
|
||||||
|
|
||||||
// Populate form
|
// Populate form
|
||||||
(document.getElementById('instance_name')).value = settings.instance_name;
|
(document.getElementById('instance_name')).value = settings.instance_name;
|
||||||
|
(document.getElementById('theme_id')).value = settings.theme_id || 'neutral';
|
||||||
(document.getElementById('platform_mode')).value = settings.platform_mode;
|
(document.getElementById('platform_mode')).value = settings.platform_mode;
|
||||||
(document.getElementById('registration_enabled')).checked = settings.registration_enabled;
|
(document.getElementById('registration_enabled')).checked = settings.registration_enabled;
|
||||||
(document.getElementById('registration_mode')).value = settings.registration_mode;
|
(document.getElementById('registration_mode')).value = settings.registration_mode;
|
||||||
|
|
@ -240,14 +254,39 @@ import { API_BASE as apiBase } from '../../lib/api';
|
||||||
|
|
||||||
if (saveBtn) saveBtn.disabled = true;
|
if (saveBtn) saveBtn.disabled = true;
|
||||||
saveStatus.textContent = 'Saving...';
|
saveStatus.textContent = 'Saving...';
|
||||||
|
saveStatus.style.color = '';
|
||||||
|
|
||||||
const data = {
|
const current = {
|
||||||
instance_name: (document.getElementById('instance_name')).value,
|
instance_name: (document.getElementById('instance_name')).value,
|
||||||
|
theme_id: (document.getElementById('theme_id')).value,
|
||||||
platform_mode: (document.getElementById('platform_mode')).value,
|
platform_mode: (document.getElementById('platform_mode')).value,
|
||||||
registration_enabled: (document.getElementById('registration_enabled')).checked,
|
registration_enabled: (document.getElementById('registration_enabled')).checked,
|
||||||
registration_mode: (document.getElementById('registration_mode')).value
|
registration_mode: (document.getElementById('registration_mode')).value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const data = {};
|
||||||
|
if (!initialSettings || current.instance_name !== initialSettings.instance_name) {
|
||||||
|
data.instance_name = current.instance_name;
|
||||||
|
}
|
||||||
|
if (!initialSettings || current.theme_id !== initialSettings.theme_id) {
|
||||||
|
data.theme_id = current.theme_id;
|
||||||
|
}
|
||||||
|
if (!initialSettings || current.platform_mode !== initialSettings.platform_mode) {
|
||||||
|
data.platform_mode = current.platform_mode;
|
||||||
|
}
|
||||||
|
if (!initialSettings || current.registration_enabled !== initialSettings.registration_enabled) {
|
||||||
|
data.registration_enabled = current.registration_enabled;
|
||||||
|
}
|
||||||
|
if (!initialSettings || current.registration_mode !== initialSettings.registration_mode) {
|
||||||
|
data.registration_mode = current.registration_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Object.keys(data).length === 0) {
|
||||||
|
saveStatus.textContent = 'No changes to save.';
|
||||||
|
if (saveBtn) saveBtn.disabled = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_BASE}/api/settings/instance`, {
|
const res = await fetch(`${API_BASE}/api/settings/instance`, {
|
||||||
method: 'PATCH',
|
method: 'PATCH',
|
||||||
|
|
@ -262,8 +301,17 @@ import { API_BASE as apiBase } from '../../lib/api';
|
||||||
throw new Error(await res.text());
|
throw new Error(await res.text());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updated = await res.json();
|
||||||
|
initialSettings = updated;
|
||||||
|
|
||||||
saveStatus.textContent = 'Saved!';
|
saveStatus.textContent = 'Saved!';
|
||||||
setTimeout(() => { saveStatus.textContent = ''; }, 3000);
|
setTimeout(() => { saveStatus.textContent = ''; }, 3000);
|
||||||
|
|
||||||
|
if (data.theme_id) {
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
saveStatus.textContent = 'Error: ' + err.message;
|
saveStatus.textContent = 'Error: ' + err.message;
|
||||||
saveStatus.style.color = '#c62828';
|
saveStatus.style.color = '#c62828';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,14 @@
|
||||||
---
|
---
|
||||||
import Layout from '../layouts/Layout.astro';
|
import Layout from '../layouts/Layout.astro';
|
||||||
import { API_BASE as apiBase } from '../lib/api';
|
import { API_BASE as apiBase } from '../lib/api';
|
||||||
|
|
||||||
|
function isEnabled(v: string | undefined): boolean {
|
||||||
|
if (!v) return false;
|
||||||
|
const n = v.trim().toLowerCase();
|
||||||
|
return n === '1' || n === 'true' || n === 'yes' || n === 'on';
|
||||||
|
}
|
||||||
|
|
||||||
|
const publicDemoSite = isEnabled((globalThis as any).process?.env?.PUBLIC_DEMO_SITE);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Settings">
|
<Layout title="Settings">
|
||||||
|
|
@ -13,7 +21,7 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
</section>
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ apiBase }}>
|
<script define:vars={{ apiBase, publicDemoSite }}>
|
||||||
import { getAllThemes, loadSavedTheme, saveTheme } from '../lib/themes';
|
import { getAllThemes, loadSavedTheme, saveTheme } from '../lib/themes';
|
||||||
|
|
||||||
const token = localStorage.getItem('token');
|
const token = localStorage.getItem('token');
|
||||||
|
|
@ -42,36 +50,50 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
|
|
||||||
const user = await res.json();
|
const user = await res.json();
|
||||||
|
|
||||||
const currentTheme = loadSavedTheme();
|
let appearanceHtml = '';
|
||||||
const themeOptions = getAllThemes()
|
if (publicDemoSite) {
|
||||||
.map((t) => {
|
appearanceHtml = `
|
||||||
const selected = t.id === currentTheme ? 'selected' : '';
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
||||||
return `<option value="${t.id}" ${selected}>${t.name}</option>`;
|
<h2>Appearance</h2>
|
||||||
})
|
<p class="hint">On the public demo, appearance is managed by the instance administrator.</p>
|
||||||
.join('');
|
|
||||||
|
|
||||||
container.innerHTML = `
|
|
||||||
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
|
||||||
<h2>Appearance</h2>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="theme-select">Theme</label>
|
|
||||||
<select id="theme-select" class="theme-select">
|
|
||||||
${themeOptions}
|
|
||||||
</select>
|
|
||||||
<p class="hint">Choose a visual theme for the interface</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
const currentTheme = loadSavedTheme();
|
||||||
|
const themeOptions = getAllThemes()
|
||||||
|
.map((t) => {
|
||||||
|
const selected = t.id === currentTheme ? 'selected' : '';
|
||||||
|
return `<option value="${t.id}" ${selected}>${t.name}</option>`;
|
||||||
|
})
|
||||||
|
.join('');
|
||||||
|
|
||||||
<div id="theme-preview" class="theme-preview">
|
appearanceHtml = `
|
||||||
<div class="preview-colors">
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
||||||
<span class="preview-swatch preview-bg" title="Background"></span>
|
<h2>Appearance</h2>
|
||||||
<span class="preview-swatch preview-surface" title="Surface"></span>
|
|
||||||
<span class="preview-swatch preview-primary" title="Primary"></span>
|
<div class="form-group">
|
||||||
<span class="preview-swatch preview-success" title="Success"></span>
|
<label for="theme-select">Theme</label>
|
||||||
<span class="preview-swatch preview-error" title="Error"></span>
|
<select id="theme-select" class="theme-select">
|
||||||
|
${themeOptions}
|
||||||
|
</select>
|
||||||
|
<p class="hint">Choose a visual theme for the interface</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="theme-preview" class="theme-preview">
|
||||||
|
<div class="preview-colors">
|
||||||
|
<span class="preview-swatch preview-bg" title="Background"></span>
|
||||||
|
<span class="preview-swatch preview-surface" title="Surface"></span>
|
||||||
|
<span class="preview-swatch preview-primary" title="Primary"></span>
|
||||||
|
<span class="preview-swatch preview-success" title="Success"></span>
|
||||||
|
<span class="preview-swatch preview-error" title="Error"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
container.innerHTML = `
|
||||||
|
${appearanceHtml}
|
||||||
|
|
||||||
<form id="profile-form" class="settings-form">
|
<form id="profile-form" class="settings-form">
|
||||||
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
<div class="form-section ui-card ui-card-pad-lg ui-form">
|
||||||
|
|
@ -99,7 +121,9 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
setupThemeSwitcher();
|
if (!publicDemoSite) {
|
||||||
|
setupThemeSwitcher();
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('profile-form')?.addEventListener('submit', async (e) => {
|
document.getElementById('profile-form')?.addEventListener('submit', async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -148,6 +172,8 @@ import { API_BASE as apiBase } from '../lib/api';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupThemeSwitcher() {
|
function setupThemeSwitcher() {
|
||||||
|
if (publicDemoSite) return;
|
||||||
|
|
||||||
function updatePreview() {
|
function updatePreview() {
|
||||||
const previewBg = document.querySelector('.preview-bg');
|
const previewBg = document.querySelector('.preview-bg');
|
||||||
const previewSurface = document.querySelector('.preview-surface');
|
const previewSurface = document.querySelector('.preview-surface');
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ if (Test-Path $envFile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Write-Host "`n[1/3] Stopping demo containers and removing volumes..." -ForegroundColor Yellow
|
Write-Host "`n[1/3] Stopping demo containers and removing volumes..." -ForegroundColor Yellow
|
||||||
podman-compose @composeArgs down --remove-orphans -v
|
podman compose @composeArgs down --remove-orphans -v
|
||||||
|
|
||||||
Write-Host "`n[2/3] Starting fresh demo instance..." -ForegroundColor Yellow
|
Write-Host "`n[2/3] Starting fresh demo instance..." -ForegroundColor Yellow
|
||||||
podman-compose @composeArgs up -d
|
podman compose @composeArgs up -d
|
||||||
|
|
||||||
Write-Host "`n[3/3] Waiting for services to be ready..." -ForegroundColor Yellow
|
Write-Host "`n[3/3] Waiting for services to be ready..." -ForegroundColor Yellow
|
||||||
Start-Sleep -Seconds 10
|
Start-Sleep -Seconds 10
|
||||||
|
|
@ -63,4 +63,4 @@ while ($retry -lt $maxRetries) {
|
||||||
|
|
||||||
Write-Host "`nWarning: Backend health check timed out. Check logs with:" -ForegroundColor Yellow
|
Write-Host "`nWarning: Backend health check timed out. Check logs with:" -ForegroundColor Yellow
|
||||||
$composeArgsText = ($composeArgs -join ' ')
|
$composeArgsText = ($composeArgs -join ' ')
|
||||||
Write-Host " podman-compose $composeArgsText logs backend"
|
Write-Host " podman compose $composeArgsText logs backend"
|
||||||
|
|
|
||||||
|
|
@ -26,10 +26,10 @@ if [ "$1" != "--force" ] && [ "$1" != "-f" ]; then
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -e "\n[1/3] Stopping demo containers and removing volumes..."
|
echo -e "\n[1/3] Stopping demo containers and removing volumes..."
|
||||||
podman-compose "${COMPOSE_ARGS[@]}" down --remove-orphans -v || docker-compose "${COMPOSE_ARGS[@]}" down --remove-orphans -v
|
podman compose "${COMPOSE_ARGS[@]}" down --remove-orphans -v || podman-compose "${COMPOSE_ARGS[@]}" down --remove-orphans -v || docker-compose "${COMPOSE_ARGS[@]}" down --remove-orphans -v
|
||||||
|
|
||||||
echo -e "\n[2/3] Starting fresh demo instance..."
|
echo -e "\n[2/3] Starting fresh demo instance..."
|
||||||
podman-compose "${COMPOSE_ARGS[@]}" up -d || docker-compose "${COMPOSE_ARGS[@]}" up -d
|
podman compose "${COMPOSE_ARGS[@]}" up -d || podman-compose "${COMPOSE_ARGS[@]}" up -d || docker-compose "${COMPOSE_ARGS[@]}" up -d
|
||||||
|
|
||||||
echo -e "\n[3/3] Waiting for services to be ready..."
|
echo -e "\n[3/3] Waiting for services to be ready..."
|
||||||
sleep 5
|
sleep 5
|
||||||
|
|
@ -54,5 +54,5 @@ while [ $retry -lt $max_retries ]; do
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -e "\nWarning: Backend health check timed out. Check logs with:"
|
echo -e "\nWarning: Backend health check timed out. Check logs with:"
|
||||||
echo " podman-compose ${COMPOSE_ARGS[*]} logs backend"
|
echo " podman compose ${COMPOSE_ARGS[*]} logs backend"
|
||||||
echo " docker-compose ${COMPOSE_ARGS[*]} logs backend"
|
echo " docker-compose ${COMPOSE_ARGS[*]} logs backend"
|
||||||
|
|
|
||||||
|
|
@ -27,19 +27,19 @@ function Invoke-Compose {
|
||||||
[Parameter(Mandatory=$true)][string[]]$Args
|
[Parameter(Mandatory=$true)][string[]]$Args
|
||||||
)
|
)
|
||||||
|
|
||||||
$podmanCompose = Get-Command podman-compose -ErrorAction SilentlyContinue
|
|
||||||
if ($podmanCompose) {
|
|
||||||
$null = & podman-compose @Args
|
|
||||||
return $LASTEXITCODE
|
|
||||||
}
|
|
||||||
|
|
||||||
$podman = Get-Command podman -ErrorAction SilentlyContinue
|
$podman = Get-Command podman -ErrorAction SilentlyContinue
|
||||||
if ($podman) {
|
if ($podman) {
|
||||||
$null = & podman compose @Args
|
$null = & podman compose @Args
|
||||||
return $LASTEXITCODE
|
return $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
throw 'Neither podman-compose nor podman was found in PATH.'
|
$podmanCompose = Get-Command podman-compose -ErrorAction SilentlyContinue
|
||||||
|
if ($podmanCompose) {
|
||||||
|
$null = & podman-compose @Args
|
||||||
|
return $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'Neither podman nor podman-compose was found in PATH.'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-CommandLine {
|
function Get-CommandLine {
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ fi
|
||||||
|
|
||||||
# Start PostgreSQL
|
# Start PostgreSQL
|
||||||
echo "Starting PostgreSQL..."
|
echo "Starting PostgreSQL..."
|
||||||
podman-compose -f "$ROOT_DIR/compose/dev.yml" up -d 2>/dev/null || true
|
podman compose -f "$ROOT_DIR/compose/dev.yml" up -d 2>/dev/null || podman-compose -f "$ROOT_DIR/compose/dev.yml" up -d 2>/dev/null || true
|
||||||
|
|
||||||
# Wait for PostgreSQL
|
# Wait for PostgreSQL
|
||||||
echo "Waiting for PostgreSQL..."
|
echo "Waiting for PostgreSQL..."
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,19 @@ function Invoke-Compose {
|
||||||
[Parameter(Mandatory=$true)][string[]]$Args
|
[Parameter(Mandatory=$true)][string[]]$Args
|
||||||
)
|
)
|
||||||
|
|
||||||
$podmanCompose = Get-Command podman-compose -ErrorAction SilentlyContinue
|
|
||||||
if ($podmanCompose) {
|
|
||||||
$null = & podman-compose @Args
|
|
||||||
return $LASTEXITCODE
|
|
||||||
}
|
|
||||||
|
|
||||||
$podman = Get-Command podman -ErrorAction SilentlyContinue
|
$podman = Get-Command podman -ErrorAction SilentlyContinue
|
||||||
if ($podman) {
|
if ($podman) {
|
||||||
$null = & podman compose @Args
|
$null = & podman compose @Args
|
||||||
return $LASTEXITCODE
|
return $LASTEXITCODE
|
||||||
}
|
}
|
||||||
|
|
||||||
throw 'Neither podman-compose nor podman was found in PATH.'
|
$podmanCompose = Get-Command podman-compose -ErrorAction SilentlyContinue
|
||||||
|
if ($podmanCompose) {
|
||||||
|
$null = & podman-compose @Args
|
||||||
|
return $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
||||||
|
throw 'Neither podman nor podman-compose was found in PATH.'
|
||||||
}
|
}
|
||||||
|
|
||||||
function Get-CommandLine {
|
function Get-CommandLine {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ pkill -f "astro dev" 2>/dev/null || true
|
||||||
# Stop PostgreSQL if requested
|
# Stop PostgreSQL if requested
|
||||||
if [ "$STOP_DB" = "--all" ] || [ "$STOP_DB" = "-a" ]; then
|
if [ "$STOP_DB" = "--all" ] || [ "$STOP_DB" = "-a" ]; then
|
||||||
echo "Stopping PostgreSQL..."
|
echo "Stopping PostgreSQL..."
|
||||||
podman-compose -f "$ROOT_DIR/compose/dev.yml" down 2>/dev/null || true
|
podman compose -f "$ROOT_DIR/compose/dev.yml" down 2>/dev/null || podman-compose -f "$ROOT_DIR/compose/dev.yml" down 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Done."
|
echo "Done."
|
||||||
|
|
|
||||||
|
|
@ -1 +1,4 @@
|
||||||
podman-compose -f compose/dev.yml up
|
podman compose -f compose/dev.yml up
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
podman-compose -f compose/dev.yml up
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,2 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
podman-compose -f compose/dev.yml up
|
podman compose -f compose/dev.yml up || podman-compose -f compose/dev.yml up
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Write-Host "=== Likwid Post-Reboot Setup ===" -ForegroundColor Cyan
|
||||||
|
|
||||||
# Step 1: Verify WSL2 is working
|
# Step 1: Verify WSL2 is working
|
||||||
Write-Host "`n[1/4] Checking WSL2 status..." -ForegroundColor Yellow
|
Write-Host "`n[1/4] Checking WSL2 status..." -ForegroundColor Yellow
|
||||||
$wslStatus = wsl --status 2>&1
|
wsl --status 2>&1 | Out-Null
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
Write-Host "WSL2 is not ready. Please ensure virtualization is enabled in BIOS." -ForegroundColor Red
|
Write-Host "WSL2 is not ready. Please ensure virtualization is enabled in BIOS." -ForegroundColor Red
|
||||||
Write-Host "Run 'wsl --install --no-distribution' as administrator if needed." -ForegroundColor Red
|
Write-Host "Run 'wsl --install --no-distribution' as administrator if needed." -ForegroundColor Red
|
||||||
|
|
@ -35,7 +35,7 @@ wsl --set-default openSUSE-Tumbleweed
|
||||||
# Step 4: Configure Podman in WSL2
|
# Step 4: Configure Podman in WSL2
|
||||||
Write-Host "`n[4/4] Configuring Podman in openSUSE Tumbleweed..." -ForegroundColor Yellow
|
Write-Host "`n[4/4] Configuring Podman in openSUSE Tumbleweed..." -ForegroundColor Yellow
|
||||||
wsl -d openSUSE-Tumbleweed -e bash -c "
|
wsl -d openSUSE-Tumbleweed -e bash -c "
|
||||||
echo 'Installing Podman and podman-compose...'
|
echo 'Installing Podman and compose support (use: podman compose)'
|
||||||
sudo zypper refresh
|
sudo zypper refresh
|
||||||
sudo zypper install -y podman podman-compose
|
sudo zypper install -y podman podman-compose
|
||||||
|
|
||||||
|
|
@ -44,7 +44,7 @@ wsl -d openSUSE-Tumbleweed -e bash -c "
|
||||||
|
|
||||||
echo 'Verifying installation...'
|
echo 'Verifying installation...'
|
||||||
podman --version
|
podman --version
|
||||||
podman-compose --version
|
podman compose --help >/dev/null 2>&1 || true
|
||||||
"
|
"
|
||||||
|
|
||||||
Write-Host "`n=== Setup Complete ===" -ForegroundColor Cyan
|
Write-Host "`n=== Setup Complete ===" -ForegroundColor Cyan
|
||||||
|
|
|
||||||
|
|
@ -28,4 +28,4 @@ if (Test-Path $demoMigration) {
|
||||||
Write-Host "`n=== Production Preparation Complete ===" -ForegroundColor Green
|
Write-Host "`n=== Production Preparation Complete ===" -ForegroundColor Green
|
||||||
Write-Host "`nNext steps:"
|
Write-Host "`nNext steps:"
|
||||||
Write-Host " 1. Configure compose/.env.production"
|
Write-Host " 1. Configure compose/.env.production"
|
||||||
Write-Host " 2. Run: podman-compose -f compose/production.yml up -d"
|
Write-Host " 2. Run: podman compose -f compose/production.yml up -d"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue