diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ef8c8dd --- /dev/null +++ b/.env.example @@ -0,0 +1,17 @@ +# Example environment variable file +# Copy this to .env or manually set the environment variables + +# Redis URL -- Used for storing session data +REDIS_URL=redis://localhost:6379 + +# PostgreSQL URL -- Used for storing data +# Option 1: Specify the URL directly +DATABASE_URL=postgresql://libretunes:password@localhost:5432/libretunes + +# Option 2: Specify the individual components +# Must specify at least POSTGRES_HOST +# POSTGRES_USER=libretunes +# POSTGRES_PASSWORD=password +# POSTGRES_HOST=localhost +# POSTGRES_PORT=5432 +# POSTGRES_DB=libretunes diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c74cbba..4954a4c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,17 +2,25 @@ build: needs: [] image: $CI_REGISTRY/libretunes/ops/docker-leptos:latest + variables: + RUSTFLAGS: "-D warnings" script: - cargo-leptos build +.docker: + image: docker:latest + services: + - docker:dind + tags: + - docker + before_script: + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + # Build the docker image and push it to the registry docker-build: needs: ["build"] - image: docker:latest + extends: .docker script: - - /usr/local/bin/dockerd-entrypoint.sh & - - while ! docker info; do echo "Waiting for Docker to become available..."; sleep 1; done - - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA . # If running on the default branch, tag as latest - if [ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]; then docker tag @@ -44,35 +52,50 @@ cargo-doc: paths: - target/doc -.argocd: - image: argoproj/argocd:v2.6.15 - before_script: - - argocd login ${ARGOCD_SERVER} --username ${ARGOCD_USERNAME} --password ${ARGOCD_PASSWORD} --grpc-web - # Start the review environment start-review: - extends: .argocd + extends: .docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual script: - - argocd app sync argocd/libretunes-review-${CI_COMMIT_SHORT_SHA} - - argocd app wait argocd/libretunes-review-${CI_COMMIT_SHORT_SHA} + - apk add curl openssl + - cd cicd + - echo "$CLOUDFLARE_TUNNEL_AUTH_JSON" > tunnel-auth.json + - ./add-dns.sh $CLOUDFLARE_ZONE_ID review-$CI_COMMIT_SHORT_SHA libretunes-auto-review $CLOUDFLARE_API_TOKEN $CLOUDFLARE_TUNNEL_ID + - ./create-tunnel-config.sh http://libretunes:3000 review-$CI_COMMIT_SHORT_SHA.libretunes.xyz $CLOUDFLARE_TUNNEL_ID + - export COMPOSE_PROJECT_NAME=review-$CI_COMMIT_SHORT_SHA + - export POSTGRES_PASSWORD=$(openssl rand -hex 16) + - export LIBRETUNES_VERSION=$CI_COMMIT_SHORT_SHA + - docker compose --file docker-compose-cicd.yml pull + - docker compose --file docker-compose-cicd.yml create + - export CONFIG_VOL_NAME=review-${CI_COMMIT_SHORT_SHA}_cloudflared-config + - export TMP_CONTAINER_NAME=$(docker run --rm -d -v $CONFIG_VOL_NAME:/data busybox sh -c "sleep infinity") + - docker cp tunnel-auth.json $TMP_CONTAINER_NAME:/data/auth.json + - docker cp cloudflared-tunnel-config.yml $TMP_CONTAINER_NAME:/data/config.yml + - docker stop $TMP_CONTAINER_NAME + - docker compose --file docker-compose-cicd.yml up -d environment: name: review/$CI_COMMIT_SHORT_SHA - url: https://review-$CI_COMMIT_SHORT_SHA.libretunes.mregirouard.com + url: https://review-$CI_COMMIT_SHORT_SHA.libretunes.xyz on_stop: stop-review + auto_stop_in: 1 week # Stop the review environment stop-review: needs: ["start-review"] - extends: .argocd + extends: .docker rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: manual allow_failure: true script: - - argocd app delete argocd/libretunes-review-${CI_COMMIT_SHORT_SHA} --cascade + - apk add jq curl + - ./cicd/remove-dns.sh $CLOUDFLARE_ZONE_ID review-$CI_COMMIT_SHORT_SHA.libretunes.xyz libretunes-auto-review $CLOUDFLARE_API_TOKEN + - export COMPOSE_PROJECT_NAME=review-$CI_COMMIT_SHORT_SHA + - export LIBRETUNES_VERSION=$CI_COMMIT_SHORT_SHA + - docker compose --file cicd/docker-compose-cicd.yml down + - docker compose --file cicd/docker-compose-cicd.yml rm -f -v environment: name: review/$CI_COMMIT_SHORT_SHA action: stop diff --git a/LICENSE b/LICENSE index e869ce3..43bb24e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 henrik +Copyright (c) 2023-2024 The LibreTunes Authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/cicd/add-dns.sh b/cicd/add-dns.sh new file mode 100755 index 0000000..7247314 --- /dev/null +++ b/cicd/add-dns.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +ZONE_ID=$1 +RECORD_NAME=$2 +RECORD_COMMENT=$3 +API_TOKEN=$4 +TUNNEL_ID=$5 + +curl --request POST --silent \ + --url https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records \ + --header 'Content-Type: application/json' \ + --header "Authorization: Bearer $API_TOKEN" \ + --data '{ + "content": "'$TUNNEL_ID'.cfargotunnel.com", + "name": "'$RECORD_NAME'", + "comment": "'$RECORD_COMMENT'", + "proxied": true, + "type": "CNAME", + "ttl": 1 +}' \ diff --git a/cicd/create-tunnel-config.sh b/cicd/create-tunnel-config.sh new file mode 100755 index 0000000..11fb59a --- /dev/null +++ b/cicd/create-tunnel-config.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +SERVICE=$1 +HOSTNAME=$2 +TUNNEL_ID=$3 + +echo "Creating tunnel config for $HOSTNAME" + +cat < cloudflared-tunnel-config.yml +tunnel: $TUNNEL_ID +credentials-file: /etc/cloudflared/auth.json + +ingress: + - hostname: $HOSTNAME + service: $SERVICE + - service: http_status:404 +EOF diff --git a/cicd/docker-compose-cicd.yml b/cicd/docker-compose-cicd.yml new file mode 100644 index 0000000..723ca41 --- /dev/null +++ b/cicd/docker-compose-cicd.yml @@ -0,0 +1,55 @@ +version: '3' + +services: + cloudflare: + image: cloudflare/cloudflared:latest + command: tunnel run + volumes: + - cloudflared-config:/etc/cloudflared:ro + + libretunes: + image: registry.mregirouard.com/libretunes/libretunes:${LIBRETUNES_VERSION} + environment: + REDIS_URL: redis://redis:6379 + POSTGRES_HOST: postgres + POSTGRES_USER: libretunes + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: libretunes + volumes: + - libretunes-audio:/site/audio + depends_on: + - redis + - postgres + restart: always + + redis: + image: redis:latest + volumes: + - libretunes-redis:/data + restart: always + healthcheck: + test: ["CMD-SHELL", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + + postgres: + image: postgres:latest + environment: + POSTGRES_USER: libretunes + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: libretunes + volumes: + - libretunes-postgres:/var/lib/postgresql/data + restart: always + healthcheck: + test: ["CMD-SHELL", "pg_isready -U libretunes"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + cloudflared-config: + libretunes-audio: + libretunes-redis: + libretunes-postgres: diff --git a/cicd/remove-dns.sh b/cicd/remove-dns.sh new file mode 100755 index 0000000..3a80869 --- /dev/null +++ b/cicd/remove-dns.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +set -e + +ZONE_ID=$1 +RECORD_NAME=$2 +RECORD_COMMENT=$3 +API_TOKEN=$4 + +RECORD_ID=$( +curl --request GET --silent \ + --url "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?name=$RECORD_NAME&comment=$RECORD_COMMENT" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $API_TOKEN" \ +| jq -r '.result[0].id') + +echo "Deleting DNS record ID $RECORD_ID" + +curl --request DELETE --silent \ + --url "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/$RECORD_ID" \ + --header "Content-Type: application/json" \ + --header "Authorization: Bearer $API_TOKEN" diff --git a/src/app.rs b/src/app.rs index f22a3c1..ad11f5f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -43,7 +43,7 @@ use crate::components::personal::*; /// Renders the home page of your application. #[component] fn HomePage() -> impl IntoView { - let mut play_status = PlayStatus::default(); + let play_status = PlayStatus::default(); let play_status = create_rw_signal(play_status); let (dashboard_open, set_dashboard_open) = create_signal(true); diff --git a/src/database.rs b/src/database.rs index cb1bee1..6ec58cc 100644 --- a/src/database.rs +++ b/src/database.rs @@ -1,8 +1,8 @@ use cfg_if::cfg_if; -use leptos::logging::log; cfg_if! { if #[cfg(feature = "ssr")] { +use leptos::logging::log; use lazy_static::lazy_static; use std::env; diff --git a/src/lib.rs b/src/lib.rs index 0a3d657..acf0690 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,6 @@ if #[cfg(feature = "hydrate")] { #[wasm_bindgen] pub fn hydrate() { use app::*; - use leptos::*; console_error_panic_hook::set_once(); diff --git a/src/models.rs b/src/models.rs index 7ad0cda..15a6d12 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,5 +1,4 @@ use std::time::SystemTime; -use std::error::Error; use time::Date; use serde::{Deserialize, Serialize}; @@ -9,6 +8,7 @@ cfg_if! { if #[cfg(feature = "ssr")] { use diesel::prelude::*; use crate::database::PgPooledConn; + use std::error::Error; } } diff --git a/src/pages/signup.rs b/src/pages/signup.rs index 503195e..20f0912 100644 --- a/src/pages/signup.rs +++ b/src/pages/signup.rs @@ -14,8 +14,6 @@ pub fn Signup() -> impl IntoView { let (show_password, set_show_password) = create_signal(false); - let navigate = leptos_router::use_navigate(); - let toggle_password = move |_| { set_show_password.update(|show_password| *show_password = !*show_password); log!("showing password"); diff --git a/src/playbar.rs b/src/playbar.rs index fcab9a7..11700aa 100644 --- a/src/playbar.rs +++ b/src/playbar.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use crate::playstatus::PlayStatus; use leptos::ev::MouseEvent; use leptos::html::{Audio, Div};