mirror of
https://github.com/rzmk/learnhouse.git
synced 2025-12-19 04:19:25 +00:00
feat: init new self-hosting method
This commit is contained in:
parent
d5791d99d5
commit
d1fde17220
11 changed files with 663 additions and 492 deletions
56
Dockerfile
Normal file
56
Dockerfile
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
# Base image
|
||||
FROM python:3.12-slim-bookworm as base
|
||||
|
||||
# Install Nginx, curl, and build-essential
|
||||
RUN apt update && apt install -y nginx curl build-essential \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& rm /etc/nginx/sites-enabled/default
|
||||
|
||||
# Install Node tools
|
||||
RUN curl -fsSL https://deb.nodesource.com/setup_21.x | bash - \
|
||||
&& apt-get install -y nodejs \
|
||||
&& npm install -g corepack pm2
|
||||
|
||||
# Frontend Build
|
||||
FROM base AS deps
|
||||
|
||||
ENV NEXT_PUBLIC_LEARNHOUSE_API_URL=http://localhost/api/v1/
|
||||
ENV NEXT_PUBLIC_LEARNHOUSE_BACKEND_URL=http://localhost/
|
||||
ENV NEXT_PUBLIC_LEARNHOUSE_DOMAIN=localhost
|
||||
ENV NEXT_PUBLIC_LEARNHOUSE_COLLABORATION_WS_URL=ws://localhost:1998
|
||||
|
||||
WORKDIR /app/web
|
||||
COPY ./apps/web/package.json ./apps/web/pnpm-lock.yaml* ./
|
||||
COPY ./apps/web /app/web
|
||||
RUN rm -f .env*
|
||||
RUN if [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile && pnpm run build; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
# Final image
|
||||
FROM base as runner
|
||||
RUN addgroup --system --gid 1001 system \
|
||||
&& adduser --system --uid 1001 app \
|
||||
&& mkdir .next \
|
||||
&& chown app:system .next
|
||||
COPY --from=deps /app/web/public ./app/web/public
|
||||
COPY --from=deps --chown=app:system /app/web/.next/standalone ./app/web/
|
||||
COPY --from=deps --chown=app:system /app/web/.next/static ./app/web/.next/static
|
||||
|
||||
# Backend Build
|
||||
WORKDIR /app/api
|
||||
COPY ./apps/api/poetry.lock* ./
|
||||
COPY ./apps/api/pyproject.toml ./
|
||||
RUN pip install --upgrade pip \
|
||||
&& pip install poetry \
|
||||
&& poetry config virtualenvs.create false \
|
||||
&& poetry install --no-interaction --no-ansi
|
||||
COPY ./apps/api ./
|
||||
|
||||
# Run the backend
|
||||
WORKDIR /app
|
||||
COPY ./extra/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
ENV PORT=8000 LEARNHOUSE_PORT=9000 HOSTNAME=0.0.0.0
|
||||
COPY ./extra/start.sh /app/start.sh
|
||||
CMD ["sh", "start.sh"]
|
||||
|
|
@ -1,9 +1,10 @@
|
|||
import random
|
||||
import string
|
||||
from typing import Annotated
|
||||
from pydantic import EmailStr
|
||||
from sqlalchemy import create_engine
|
||||
from sqlmodel import SQLModel, Session
|
||||
import typer
|
||||
|
||||
from config.config import get_learnhouse_config
|
||||
from src.db.organizations import OrganizationCreate
|
||||
from src.db.users import UserCreate
|
||||
|
|
@ -15,6 +16,10 @@ from src.services.install.install import (
|
|||
|
||||
cli = typer.Typer()
|
||||
|
||||
def generate_password(length):
|
||||
characters = string.ascii_uppercase + string.ascii_lowercase + string.digits
|
||||
password = ''.join(random.choice(characters) for _ in range(length))
|
||||
return password
|
||||
|
||||
@cli.command()
|
||||
def install(
|
||||
|
|
@ -49,8 +54,10 @@ def install(
|
|||
|
||||
# Create Organization User
|
||||
print("Creating default organization user...")
|
||||
# Generate random 6 digit password
|
||||
password = generate_password(8)
|
||||
user = UserCreate(
|
||||
username="admin", email=EmailStr("admin@school.io"), password="adminsecret"
|
||||
username="admin", email=EmailStr("admin@school.dev"), password=password
|
||||
)
|
||||
install_create_organization_user(user, "default", db_session)
|
||||
print("Default organization user created ✅")
|
||||
|
|
@ -60,7 +67,8 @@ def install(
|
|||
print("")
|
||||
print("Login with the following credentials:")
|
||||
print("email: admin@school.io")
|
||||
print("password: adminsecret")
|
||||
print("password: " + password)
|
||||
print("⚠️ Remember to change the password after logging in ⚠️")
|
||||
|
||||
else:
|
||||
# Install the default elements
|
||||
|
|
|
|||
948
apps/api/poetry.lock
generated
948
apps/api/poetry.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,7 +12,6 @@ version = "0.1.0"
|
|||
[tool.poetry.dependencies]
|
||||
boto3 = "^1.34.79"
|
||||
botocore = "^1.34.84"
|
||||
chromadb = "^0.4.22"
|
||||
faker = "^24.9.0"
|
||||
fastapi = "^0.110.1"
|
||||
fastapi-jwt-auth = "^0.5.0"
|
||||
|
|
@ -38,6 +37,7 @@ sqlmodel = "^0.0.16"
|
|||
tiktoken = "^0.6.0"
|
||||
uvicorn = "0.29.0"
|
||||
typer = "^0.12.3"
|
||||
chromadb = "^0.4.24"
|
||||
|
||||
[build-system]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
|||
37
apps/api/src/core/events/autoinstall.py
Normal file
37
apps/api/src/core/events/autoinstall.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from sqlalchemy import create_engine
|
||||
from sqlmodel import SQLModel, Session, select
|
||||
|
||||
from cli import install
|
||||
from config.config import get_learnhouse_config
|
||||
from src.db.organizations import Organization
|
||||
|
||||
|
||||
def auto_install():
|
||||
# Get the database session
|
||||
learnhouse_config = get_learnhouse_config()
|
||||
engine = create_engine(
|
||||
learnhouse_config.database_config.sql_connection_string, echo=False, pool_pre_ping=True # type: ignore
|
||||
)
|
||||
SQLModel.metadata.create_all(engine)
|
||||
|
||||
db_session = Session(engine)
|
||||
|
||||
orgs = db_session.exec(select(Organization)).all()
|
||||
|
||||
if len(orgs) == 0:
|
||||
print("No organizations found. Starting auto-installation 🏗️")
|
||||
install(short=True)
|
||||
|
||||
if orgs:
|
||||
for org in orgs:
|
||||
default_org = db_session.exec(select(Organization).where(Organization.slug == 'default')).first()
|
||||
|
||||
if not default_org:
|
||||
print("No default organization found. Starting auto-installation 🏗️")
|
||||
install(short=True)
|
||||
|
||||
else:
|
||||
print("Organizations found. Skipping auto-installation 🚀")
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Callable
|
||||
from fastapi import FastAPI
|
||||
from config.config import LearnHouseConfig, get_learnhouse_config
|
||||
from src.core.events.autoinstall import auto_install
|
||||
from src.core.events.content import check_content_directory
|
||||
from src.core.events.database import close_database, connect_to_db
|
||||
from src.core.events.logs import create_logs_dir
|
||||
|
|
@ -25,6 +26,9 @@ def startup_app(app: FastAPI) -> Callable:
|
|||
# Create content directory
|
||||
await check_content_directory()
|
||||
|
||||
# Check if auto-installation is needed
|
||||
auto_install()
|
||||
|
||||
return start_app
|
||||
|
||||
|
||||
|
|
|
|||
19
apps/web/app/api/revalidate/route.ts
Normal file
19
apps/web/app/api/revalidate/route.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { revalidateTag } from 'next/cache'
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const tag: any = request.nextUrl.searchParams.get('tag')
|
||||
revalidateTag(tag)
|
||||
|
||||
return NextResponse.json(
|
||||
{ revalidated: true, now: Date.now(), tag },
|
||||
{
|
||||
status: 200,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -1,17 +1,13 @@
|
|||
version: "3.9"
|
||||
services:
|
||||
api:
|
||||
build: apps/api/.
|
||||
app:
|
||||
build: .
|
||||
ports:
|
||||
- "1338:80"
|
||||
- "80:80"
|
||||
volumes:
|
||||
- .:/usr/learnhouse
|
||||
environment:
|
||||
- LEARNHOUSE_COOKIE_DOMAIN=.localhost
|
||||
# This overrides the default config.yaml (optimized for docker container based development)
|
||||
- LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse:learnhouse@db:5432/learnhouse
|
||||
- LEARNHOUSE_REDIS_CONNECTION_STRING=redis://redis:6379/learnhouse
|
||||
- LEARNHOUSE_PORT=80
|
||||
env_file:
|
||||
- ./extra/example-learnhouse-conf.env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
7
extra/example-learnhouse-conf.env
Normal file
7
extra/example-learnhouse-conf.env
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Frontend
|
||||
NEXT_PUBLIC_LEARNHOUSE_MULTI_ORG=false
|
||||
NEXT_PUBLIC_LEARNHOUSE_DEFAULT_ORG=default
|
||||
# Backend
|
||||
LEARNHOUSE_COOKIE_DOMAIN=.localhost
|
||||
LEARNHOUSE_SQL_CONNECTION_STRING=postgresql://learnhouse:learnhouse@db:5432/learnhouse
|
||||
LEARNHOUSE_REDIS_CONNECTION_STRING=redis://redis:6379/learnhouse
|
||||
40
extra/nginx.conf
Normal file
40
extra/nginx.conf
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
# NextJS Revalidation
|
||||
location /api/revalidate {
|
||||
proxy_pass http://localhost:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Python Backend API
|
||||
location /api {
|
||||
proxy_pass http://localhost:9000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Backend Static Content
|
||||
location /content {
|
||||
proxy_pass http://localhost:9000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
|
||||
# Frontend
|
||||
location / {
|
||||
proxy_pass http://localhost:8000;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
14
extra/start.sh
Normal file
14
extra/start.sh
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Start the services
|
||||
pm2 start server.js --cwd /app/web --name learnhouse-web > /dev/null 2>&1
|
||||
pm2 start app.py --cwd /app/api --name learnhouse-api > /dev/null 2>&1
|
||||
|
||||
# Check if the services are running qnd log the status
|
||||
pm2 status
|
||||
|
||||
# Start Nginx in the background
|
||||
nginx -g 'daemon off;' &
|
||||
|
||||
# Tail Nginx error and access logs
|
||||
pm2 logs
|
||||
Loading…
Add table
Add a link
Reference in a new issue