mirror of
https://github.com/mediacms-io/mediacms.git
synced 2025-11-20 05:36:03 -05:00
Compare commits
270 Commits
v1.0
...
feat-docke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66e67c751e | ||
|
|
9b3d9fe1e7 | ||
|
|
ea340b6a2e | ||
|
|
ba2c31b1e6 | ||
|
|
5eb6fafb8c | ||
|
|
c035bcddf5 | ||
|
|
01912ea1f9 | ||
|
|
d9f299af4d | ||
|
|
e80590a3aa | ||
|
|
2a0cb977f2 | ||
|
|
a5e6e7b9ca | ||
|
|
b39072c8ae | ||
|
|
f4ab60e894 | ||
|
|
8656b40c5b | ||
|
|
553a25a86f | ||
|
|
1c1af489f1 | ||
|
|
c4c5ecf06a | ||
|
|
725cc71960 | ||
|
|
0c1c5bbb09 | ||
|
|
56182f0a6d | ||
|
|
208f0b338b | ||
|
|
cbef629baf | ||
|
|
8e8454d8c2 | ||
|
|
8d982ace92 | ||
|
|
6cee02085c | ||
|
|
e33aa17911 | ||
|
|
a8db23f204 | ||
|
|
d6428e3334 | ||
|
|
fd342fd1d6 | ||
|
|
7a1b32f1ba | ||
|
|
817e16ac60 | ||
|
|
8cbeb72dd2 | ||
|
|
e9f862a0ff | ||
|
|
02eac68b51 | ||
|
|
e790795bfd | ||
|
|
de99d84c18 | ||
|
|
8aa89c0958 | ||
|
|
df98b65704 | ||
|
|
a607996bfa | ||
|
|
79f2e2bb11 | ||
|
|
d54732040a | ||
|
|
e8520bc7cd | ||
|
|
b6e46e7b62 | ||
|
|
36eab954bd | ||
|
|
610716533b | ||
|
|
4f1c4a2b4c | ||
|
|
83f3eec940 | ||
|
|
a5acce4ab1 | ||
|
|
a4e9309350 | ||
|
|
6beaf0bbe2 | ||
|
|
70168299ba | ||
|
|
b28c2d8271 | ||
|
|
d34fc328bf | ||
|
|
ab4d9d67df | ||
|
|
f7a2f049bd | ||
|
|
05414f66c7 | ||
|
|
8fecccce1c | ||
|
|
2a7123ca0b | ||
|
|
20f305e69e | ||
|
|
d1fda05fdc | ||
|
|
a02e0a8a66 | ||
|
|
21f76dbb6e | ||
|
|
50e9f3103f | ||
|
|
0b9a203123 | ||
|
|
5cbd815496 | ||
|
|
3a8cacc847 | ||
|
|
5402ee7bc5 | ||
|
|
a6a2b50c8d | ||
|
|
23e48a8bb7 | ||
|
|
313cd9cbc6 | ||
|
|
0392dbe1ed | ||
|
|
a7562c244e | ||
|
|
d2ee12087c | ||
|
|
6db01932e1 | ||
|
|
53d8215346 | ||
|
|
1b960b28f8 | ||
|
|
02d9188aa1 | ||
|
|
8d9a4618f0 | ||
|
|
cf93a77802 | ||
|
|
5a1e4f25ed | ||
|
|
9fc7597e73 | ||
|
|
9b3e0250d4 | ||
|
|
1384471745 | ||
|
|
29b362c8ce | ||
|
|
b8ee2e9fb8 | ||
|
|
99be0f07dd | ||
|
|
27d1660192 | ||
|
|
98adb22205 | ||
|
|
673ddeb5bd | ||
|
|
aa8a2d92dc | ||
|
|
6bbd4c2809 | ||
|
|
c4148bd504 | ||
|
|
ea8b2af26f | ||
|
|
5aa899cef0 | ||
|
|
4992cc425c | ||
|
|
ef4067cbdd | ||
|
|
8cc3513a8a | ||
|
|
90e593946d | ||
|
|
f7136e2a11 | ||
|
|
0151e834a1 | ||
|
|
5fe4d3a9fc | ||
|
|
94c646fdb8 | ||
|
|
d665058b80 | ||
|
|
986c7d1074 | ||
|
|
1adee8c156 | ||
|
|
ffd7a52863 | ||
|
|
c5047d8df8 | ||
|
|
dcbfaca91c | ||
|
|
918df010f5 | ||
|
|
e9739bab45 | ||
|
|
e7ce9ef5c0 | ||
|
|
4829adf110 | ||
|
|
fdff0811a1 | ||
|
|
92c0ff579a | ||
|
|
847cff2b5c | ||
|
|
e8d3ff25be | ||
|
|
15d217453b | ||
|
|
029665145e | ||
|
|
487e098b96 | ||
|
|
fe7427a1f2 | ||
|
|
4bf41fe80e | ||
|
|
1fd04ca947 | ||
|
|
a1962d4b32 | ||
|
|
6e9c9ed81f | ||
|
|
51186e3253 | ||
|
|
150967b342 | ||
|
|
bb6244d862 | ||
|
|
a002422b77 | ||
|
|
24167b9624 | ||
|
|
b9db1a5e2e | ||
|
|
296aeac567 | ||
|
|
10c386f886 | ||
|
|
367faaddd1 | ||
|
|
3d59b87f09 | ||
|
|
5dee41de39 | ||
|
|
08bba5fc05 | ||
|
|
102414b514 | ||
|
|
c866fdd6ba | ||
|
|
5b601698a4 | ||
|
|
f040f73f51 | ||
|
|
b7a70d92fa | ||
|
|
2f43cef8da | ||
|
|
ad633e6fdf | ||
|
|
cd8d0ea49a | ||
|
|
a3997bfb1c | ||
|
|
4b0718c43f | ||
|
|
91d8179fa0 | ||
|
|
6532b19849 | ||
|
|
6ea8fd12a3 | ||
|
|
d971bb955f | ||
|
|
b52b008f89 | ||
|
|
30cf5d7176 | ||
|
|
6fd9a7d37f | ||
|
|
9c6d13559b | ||
|
|
8ec97a8219 | ||
|
|
de8f9ca718 | ||
|
|
a4bedca4db | ||
|
|
da565b3bfc | ||
|
|
239ff6cb60 | ||
|
|
da840b156d | ||
|
|
b08d493823 | ||
|
|
25eaa35758 | ||
|
|
cba2ed75ed | ||
|
|
de5bd07a90 | ||
|
|
34c1a4e33e | ||
|
|
80be0d06e5 | ||
|
|
a6322fae6d | ||
|
|
3d4dd27220 | ||
|
|
f6a78dd0b4 | ||
|
|
cbc9633fe2 | ||
|
|
3e7b106482 | ||
|
|
0f182c2b15 | ||
|
|
1ddfae7c95 | ||
|
|
f1969e4637 | ||
|
|
9e7a7a7482 | ||
|
|
6e478e6e82 | ||
|
|
e06deed3b8 | ||
|
|
f8376c5c58 | ||
|
|
e7ae2833d9 | ||
|
|
fb0f3ee739 | ||
|
|
c0701de047 | ||
|
|
0d4918a715 | ||
|
|
8093c4ccb5 | ||
|
|
2dbd97cb22 | ||
|
|
6b6662420f | ||
|
|
f1a1e342db | ||
|
|
738247c32b | ||
|
|
f974d78270 | ||
|
|
28031f07e5 | ||
|
|
4480fa7de1 | ||
|
|
32e07035f3 | ||
|
|
2ce8dba163 | ||
|
|
48838ac406 | ||
|
|
062e4be6c2 | ||
|
|
fb00f94bfa | ||
|
|
755df50c41 | ||
|
|
ba94989e6a | ||
|
|
86cc0442d8 | ||
|
|
2dde4257f7 | ||
|
|
3afff52ebf | ||
|
|
c27e3caff6 | ||
|
|
16e2c32d17 | ||
|
|
f4f6fa5962 | ||
|
|
b9e35c66a3 | ||
|
|
360a647eb5 | ||
|
|
7237040777 | ||
|
|
aa6520daac | ||
|
|
060bb45725 | ||
|
|
1f0cc4ff87 | ||
|
|
c28a39fa47 | ||
|
|
d17b3b4153 | ||
|
|
950adcdd9d | ||
|
|
235efbe151 | ||
|
|
8145ba0914 | ||
|
|
f74d3c4b57 | ||
|
|
9b9a718a18 | ||
|
|
26804dce40 | ||
|
|
de30fe68f1 | ||
|
|
4f72c00598 | ||
|
|
d3a3934ce7 | ||
|
|
ddbaa51285 | ||
|
|
7fae5992e7 | ||
|
|
46384a3c49 | ||
|
|
cdcf148b72 | ||
|
|
09e565e99b | ||
|
|
7bda0acd8b | ||
|
|
748d4bae4b | ||
|
|
171b9f84d7 | ||
|
|
853b28130d | ||
|
|
5602422d29 | ||
|
|
110695ae2f | ||
|
|
6df942ac4e | ||
|
|
2d49b1df29 | ||
|
|
8f228d6844 | ||
|
|
94b26a8781 | ||
|
|
9002930994 | ||
|
|
1e6ee280ca | ||
|
|
cf278211fb | ||
|
|
2226b6afbf | ||
|
|
adf3d4377f | ||
|
|
be41c6876d | ||
|
|
784a18ad27 | ||
|
|
6a5c57f2b2 | ||
|
|
10f198fff3 | ||
|
|
6b89d9722b | ||
|
|
04f59ffbb8 | ||
|
|
632db06ca3 | ||
|
|
0129ab6732 | ||
|
|
2b65afc8dd | ||
|
|
b27e3ca6f6 | ||
|
|
1a7adb80da | ||
|
|
2552551662 | ||
|
|
3b35ce0262 | ||
|
|
883af9bb4a | ||
|
|
47f2279098 | ||
|
|
c3d411ede1 | ||
|
|
4450350dba | ||
|
|
ba1bf7d263 | ||
|
|
41c66469e2 | ||
|
|
9d1a22e4a9 | ||
|
|
17eae8eefb | ||
|
|
86d819d1f6 | ||
|
|
58d703c3eb | ||
|
|
8473b505c3 | ||
|
|
952665beab | ||
|
|
ac3ce569a8 | ||
|
|
fec21c2f40 | ||
|
|
a15ed70d44 | ||
|
|
087206346a | ||
|
|
b2fcecf5ab |
4
.coveragerc
Normal file
4
.coveragerc
Normal file
@@ -0,0 +1,4 @@
|
||||
[run]
|
||||
omit =
|
||||
*bento4*
|
||||
*/migrations/*
|
||||
113
.docker-backup/Dockerfile
Normal file
113
.docker-backup/Dockerfile
Normal file
@@ -0,0 +1,113 @@
|
||||
FROM python:3.13.5-slim-bookworm AS build-image
|
||||
|
||||
# Install system dependencies needed for downloading and extracting
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y --no-install-recommends wget xz-utils unzip && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt-get purge --auto-remove && \
|
||||
apt-get clean
|
||||
|
||||
RUN wget -q https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
|
||||
|
||||
RUN mkdir -p ffmpeg-tmp && \
|
||||
tar -xf ffmpeg-release-amd64-static.tar.xz --strip-components 1 -C ffmpeg-tmp && \
|
||||
cp -v ffmpeg-tmp/ffmpeg ffmpeg-tmp/ffprobe ffmpeg-tmp/qt-faststart /usr/local/bin && \
|
||||
rm -rf ffmpeg-tmp ffmpeg-release-amd64-static.tar.xz
|
||||
|
||||
# Install Bento4 in the specified location
|
||||
RUN mkdir -p /home/mediacms.io/bento4 && \
|
||||
wget -q http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip && \
|
||||
unzip Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip -d /home/mediacms.io/bento4 && \
|
||||
mv /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/* /home/mediacms.io/bento4/ && \
|
||||
rm -rf /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux && \
|
||||
rm -rf /home/mediacms.io/bento4/docs && \
|
||||
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
|
||||
|
||||
############ BASE RUNTIME IMAGE ############
|
||||
FROM python:3.13.5-slim-bookworm AS base
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV PYTHONDONTWRITEBYTECODE=1
|
||||
ENV CELERY_APP='cms'
|
||||
ENV VIRTUAL_ENV=/home/mediacms.io
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
# Install system dependencies first
|
||||
RUN apt-get update -y && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get install --no-install-recommends -y \
|
||||
supervisor \
|
||||
nginx \
|
||||
imagemagick \
|
||||
procps \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
zlib1g-dev \
|
||||
zlib1g \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libxmlsec1-openssl \
|
||||
libpq-dev \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Set up virtualenv first
|
||||
RUN mkdir -p /home/mediacms.io/mediacms/{logs} && \
|
||||
cd /home/mediacms.io && \
|
||||
python3 -m venv $VIRTUAL_ENV
|
||||
|
||||
# Copy requirements files
|
||||
COPY requirements.txt requirements-dev.txt ./
|
||||
|
||||
# Install Python dependencies using pip (within virtualenv)
|
||||
ARG DEVELOPMENT_MODE=False
|
||||
RUN pip install --no-cache-dir uv && \
|
||||
uv pip install --no-binary lxml --no-binary xmlsec -r requirements.txt && \
|
||||
if [ "$DEVELOPMENT_MODE" = "True" ]; then \
|
||||
echo "Installing development dependencies..." && \
|
||||
uv pip install -r requirements-dev.txt; \
|
||||
fi && \
|
||||
apt-get purge -y --auto-remove \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libpq-dev
|
||||
|
||||
# Copy ffmpeg and Bento4 from build image
|
||||
COPY --from=build-image /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
|
||||
COPY --from=build-image /usr/local/bin/ffprobe /usr/local/bin/ffprobe
|
||||
COPY --from=build-image /usr/local/bin/qt-faststart /usr/local/bin/qt-faststart
|
||||
COPY --from=build-image /home/mediacms.io/bento4 /home/mediacms.io/bento4
|
||||
|
||||
# Copy application files
|
||||
COPY . /home/mediacms.io/mediacms
|
||||
WORKDIR /home/mediacms.io/mediacms
|
||||
|
||||
# required for sprite thumbnail generation for large video files
|
||||
COPY deploy/docker/policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
# Set process control environment variables
|
||||
ENV ENABLE_UWSGI='yes' \
|
||||
ENABLE_NGINX='yes' \
|
||||
ENABLE_CELERY_BEAT='yes' \
|
||||
ENABLE_CELERY_SHORT='yes' \
|
||||
ENABLE_CELERY_LONG='yes' \
|
||||
ENABLE_MIGRATIONS='yes'
|
||||
|
||||
EXPOSE 9000 80
|
||||
|
||||
RUN chmod +x ./deploy/docker/entrypoint.sh
|
||||
|
||||
ENTRYPOINT ["./deploy/docker/entrypoint.sh"]
|
||||
CMD ["./deploy/docker/start.sh"]
|
||||
|
||||
############ FULL IMAGE ############
|
||||
FROM base AS full
|
||||
COPY requirements-full.txt ./
|
||||
RUN mkdir -p /root/.cache/ && \
|
||||
chmod go+rwx /root/ && \
|
||||
chmod go+rwx /root/.cache/
|
||||
RUN uv pip install -r requirements-full.txt
|
||||
119
.docker-backup/docker-compose-cert.yaml
Normal file
119
.docker-backup/docker-compose-cert.yaml
Normal file
@@ -0,0 +1,119 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
container_name: nginx-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- conf:/etc/nginx/conf.d
|
||||
- vhost:/etc/nginx/vhost.d
|
||||
- html:/usr/share/nginx/html
|
||||
- dhparam:/etc/nginx/dhparam
|
||||
- certs:/etc/nginx/certs:ro
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
|
||||
acme-companion:
|
||||
image: nginxproxy/acme-companion
|
||||
container_name: nginx-proxy-acme
|
||||
volumes_from:
|
||||
- nginx-proxy
|
||||
volumes:
|
||||
- certs:/etc/nginx/certs:rw
|
||||
- acme:/etc/acme.sh
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'Y'
|
||||
ADMIN_PASSWORD: 'X'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
VIRTUAL_HOST: 'X.mediacms.io'
|
||||
LETSENCRYPT_HOST: 'X.mediacms.io'
|
||||
LETSENCRYPT_EMAIL: 'X'
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:full
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
volumes:
|
||||
conf:
|
||||
vhost:
|
||||
html:
|
||||
dhparam:
|
||||
certs:
|
||||
acme:
|
||||
89
.docker-backup/docker-compose-dev.yaml
Normal file
89
.docker-backup/docker-compose-dev.yaml
Normal file
@@ -0,0 +1,89 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
migrations:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile
|
||||
target: base
|
||||
args:
|
||||
- DEVELOPMENT_MODE=True
|
||||
image: mediacms/mediacms-dev:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
environment:
|
||||
DEVELOPMENT_MODE: True
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
frontend:
|
||||
image: node:20
|
||||
volumes:
|
||||
- ${PWD}/frontend:/home/mediacms.io/mediacms/frontend/
|
||||
working_dir: /home/mediacms.io/mediacms/frontend/
|
||||
command: bash -c "npm install && npm run start"
|
||||
env_file:
|
||||
- ${PWD}/frontend/.env
|
||||
ports:
|
||||
- "8088:8088"
|
||||
depends_on:
|
||||
- web
|
||||
web:
|
||||
image: mediacms/mediacms-dev:latest
|
||||
command: "python manage.py runserver 0.0.0.0:80"
|
||||
environment:
|
||||
DEVELOPMENT_MODE: True
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
celery_worker:
|
||||
image: mediacms/mediacms-dev:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- web
|
||||
86
.docker-backup/docker-compose.yaml
Normal file
86
.docker-backup/docker-compose.yaml
Normal file
@@ -0,0 +1,86 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
# ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
37
.dockerignore
Normal file
37
.dockerignore
Normal file
@@ -0,0 +1,37 @@
|
||||
# Dependencies
|
||||
node_modules
|
||||
npm-debug.log
|
||||
|
||||
# Local development files - exclude uploaded content but keep placeholder images
|
||||
media_files/*
|
||||
!media_files/userlogos/
|
||||
media_files/userlogos/*
|
||||
!media_files/userlogos/*.jpg
|
||||
logs
|
||||
static_collected
|
||||
|
||||
# Version control
|
||||
.git
|
||||
.github
|
||||
.gitignore
|
||||
|
||||
# Development/testing
|
||||
.pytest_cache
|
||||
.qodo
|
||||
.claude
|
||||
|
||||
# Docker
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
docker-compose*.yml
|
||||
.docker-backup
|
||||
|
||||
# Documentation (if you don't need it in the image)
|
||||
docs
|
||||
|
||||
# Other
|
||||
*.pyc
|
||||
__pycache__
|
||||
.env
|
||||
.vscode
|
||||
.idea
|
||||
3
.github/FUNDING.yml
vendored
Normal file
3
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [mgogoulos]
|
||||
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: Issue report
|
||||
about: Create a report to help us improve MediaCMS
|
||||
title: ''
|
||||
labels: 'issue: bug'
|
||||
assignees: mgogoulos
|
||||
|
||||
---
|
||||
|
||||
**Describe the issue**
|
||||
A clear and concise description of what the issue is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the issue:
|
||||
1. Go to ...
|
||||
2. Perform action ...
|
||||
3. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Environment (please complete the following information):**
|
||||
- OS: [e.g. Ubuntu Linux]
|
||||
- Installation method: [Docker install, or single server install]
|
||||
- Browser, if applicable
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea
|
||||
title: ''
|
||||
labels: 'issue: enhancement'
|
||||
assignees: mgogoulos
|
||||
|
||||
---
|
||||
|
||||
**Describe the feature you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
10
.github/pull_request_template.md
vendored
Normal file
10
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
## Description
|
||||
<!-- Describe the changes introduced by this PR for the reviewers to fully understand. -->
|
||||
|
||||
|
||||
## Steps
|
||||
<!-- Actions to be done pre and post deployment -->
|
||||
*Pre-deploy*
|
||||
|
||||
*Post-deploy*
|
||||
|
||||
20
.github/workflows/ci.yml
vendored
Normal file
20
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "CI"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '**/README.md'
|
||||
jobs:
|
||||
pre-commit:
|
||||
uses: ./.github/workflows/pre-commit.yml
|
||||
test:
|
||||
uses: ./.github/workflows/python.yml
|
||||
needs: [pre-commit]
|
||||
release:
|
||||
uses: ./.github/workflows/docker-build-push.yml
|
||||
secrets: inherit # pass all secrets
|
||||
needs: [test]
|
||||
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
||||
134
.github/workflows/docker-build-push.yml
vendored
Normal file
134
.github/workflows/docker-build-push.yml
vendored
Normal file
@@ -0,0 +1,134 @@
|
||||
name: Docker build and push
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
push:
|
||||
tags:
|
||||
- v*.*.*
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Build & release to DockerHub
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2.2.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Docker meta for web image
|
||||
id: meta-web
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
mediacms/mediacms
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
labels: |
|
||||
org.opencontainers.image.title=MediaCMS
|
||||
org.opencontainers.image.description=MediaCMS is a modern, fully featured open source video and media CMS, written in Python/Django and React, featuring a REST API.
|
||||
org.opencontainers.image.vendor=MediaCMS
|
||||
org.opencontainers.image.url=https://mediacms.io/
|
||||
org.opencontainers.image.source=https://github.com/mediacms-io/mediacms
|
||||
org.opencontainers.image.licenses=AGPL-3.0
|
||||
|
||||
- name: Build and push web image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
target: web
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta-web.outputs.tags }}
|
||||
labels: ${{ steps.meta-web.outputs.labels }}
|
||||
|
||||
- name: Docker meta for worker image
|
||||
id: meta-worker
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
mediacms/mediacms-worker
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
labels: |
|
||||
org.opencontainers.image.title=MediaCMS Worker
|
||||
org.opencontainers.image.description=MediaCMS Celery worker for background task processing.
|
||||
org.opencontainers.image.vendor=MediaCMS
|
||||
org.opencontainers.image.url=https://mediacms.io/
|
||||
org.opencontainers.image.source=https://github.com/mediacms-io/mediacms
|
||||
org.opencontainers.image.licenses=AGPL-3.0
|
||||
|
||||
- name: Build and push worker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
target: worker
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta-worker.outputs.tags }}
|
||||
labels: ${{ steps.meta-worker.outputs.labels }}
|
||||
|
||||
- name: Docker meta for worker-full image
|
||||
id: meta-worker-full
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
mediacms/mediacms-worker
|
||||
tags: |
|
||||
type=raw,value=latest-full,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=semver,pattern={{version}}-full
|
||||
type=semver,pattern={{major}}.{{minor}}-full
|
||||
type=semver,pattern={{major}}-full
|
||||
labels: |
|
||||
org.opencontainers.image.title=MediaCMS Worker Full
|
||||
org.opencontainers.image.description=MediaCMS Celery worker with additional codecs for advanced transcoding features.
|
||||
org.opencontainers.image.vendor=MediaCMS
|
||||
org.opencontainers.image.url=https://mediacms.io/
|
||||
org.opencontainers.image.source=https://github.com/mediacms-io/mediacms
|
||||
org.opencontainers.image.licenses=AGPL-3.0
|
||||
|
||||
- name: Build and push worker-full image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
target: worker-full
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta-worker-full.outputs.tags }}
|
||||
labels: ${{ steps.meta-worker-full.outputs.labels }}
|
||||
|
||||
- name: Docker meta for nginx image
|
||||
id: meta-nginx
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: |
|
||||
mediacms/mediacms-nginx
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
labels: |
|
||||
org.opencontainers.image.title=MediaCMS Nginx
|
||||
org.opencontainers.image.description=Nginx web server for MediaCMS, serving static and media files.
|
||||
org.opencontainers.image.vendor=MediaCMS
|
||||
org.opencontainers.image.url=https://mediacms.io/
|
||||
org.opencontainers.image.source=https://github.com/mediacms-io/mediacms
|
||||
org.opencontainers.image.licenses=AGPL-3.0
|
||||
|
||||
- name: Build and push nginx image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
file: Dockerfile.nginx
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta-nginx.outputs.tags }}
|
||||
labels: ${{ steps.meta-nginx.outputs.labels }}
|
||||
15
.github/workflows/pre-commit.yml
vendored
Normal file
15
.github/workflows/pre-commit.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: pre-commit
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
pre-commit:
|
||||
name: Pre-Commit
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-python@v3
|
||||
- uses: pre-commit/action@v3.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
35
.github/workflows/python.yml
vendored
Normal file
35
.github/workflows/python.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
name: Python Tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build & test via docker-compose
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Build the Stack
|
||||
run: docker compose -f docker-compose-dev.yaml build
|
||||
|
||||
- name: Start containers
|
||||
run: docker compose -f docker-compose-dev.yaml up -d
|
||||
|
||||
- name: List containers
|
||||
run: docker ps
|
||||
|
||||
- name: Sleep for 60 seconds
|
||||
run: sleep 60s
|
||||
shell: bash
|
||||
|
||||
- name: Run Django Tests
|
||||
run: docker compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest
|
||||
|
||||
# Run with coverage, saves report on htmlcov dir
|
||||
# run: docker-compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest --cov --cov-report=html --cov-config=.coveragerc
|
||||
|
||||
- name: Tear down the Stack
|
||||
run: docker compose -f docker-compose-dev.yaml down
|
||||
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
cli-tool/.env
|
||||
frontend/package-lock.json
|
||||
custom/local_settings.py
|
||||
custom/static/images/*
|
||||
!custom/static/images/.gitkeep
|
||||
custom/static/css/*
|
||||
!custom/static/css/.gitkeep
|
||||
media_files/encoded/
|
||||
media_files/original/
|
||||
media_files/hls/
|
||||
media_files/chunks/
|
||||
media_files/uploads/
|
||||
media_files/tinymce_media/
|
||||
postgres_data/
|
||||
celerybeat-schedule
|
||||
logs/
|
||||
pids/
|
||||
static/admin/
|
||||
static/ckeditor/
|
||||
static/debug_toolbar/
|
||||
static/mptt/
|
||||
static/rest_framework/
|
||||
static/drf-yasg
|
||||
cms/local_settings.py
|
||||
config/local_settings.py
|
||||
yt.readme.md
|
||||
/frontend-tools/video-editor/node_modules
|
||||
/frontend-tools/video-editor/client/node_modules
|
||||
/static_collected
|
||||
/frontend-tools/video-editor-v1
|
||||
frontend-tools/.DS_Store
|
||||
static/video_editor/videos/sample-video-30s.mp4
|
||||
static/video_editor/videos/sample-video-37s.mp4
|
||||
/frontend-tools/video-editor-v2
|
||||
.DS_Store
|
||||
static/video_editor/videos/sample-video-10m.mp4
|
||||
static/video_editor/videos/sample-video-10s.mp4
|
||||
frontend-tools/video-js/public/videos/sample-video-white.mp4
|
||||
frontend-tools/video-editor/client/public/videos/sample-video.mp3
|
||||
frontend-tools/chapters-editor/client/public/videos/sample-video.mp3
|
||||
static/chapters_editor/videos/sample-video.mp3
|
||||
static/video_editor/videos/sample-video.mp3
|
||||
backups/
|
||||
16
.pre-commit-config.yaml
Normal file
16
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
repos:
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 6.1.0
|
||||
hooks:
|
||||
- id: flake8
|
||||
- repo: https://github.com/pycqa/isort
|
||||
rev: 5.12.0
|
||||
hooks:
|
||||
- id: isort
|
||||
args: ["--profile", "black"]
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
language_version: python3
|
||||
additional_dependencies: [ 'click==8.0.4' ]
|
||||
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@@ -0,0 +1,3 @@
|
||||
/templates/cms/*
|
||||
/templates/*.html
|
||||
*.scss
|
||||
21
.prettierrc
Normal file
21
.prettierrc
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"printWidth": 120,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"arrowParens": "always",
|
||||
"endOfLine": "lf",
|
||||
"embeddedLanguageFormatting": "auto",
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.css", "*.scss"],
|
||||
"options": {
|
||||
"singleQuote": false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1 @@
|
||||
Wordgames.gr - https://www.wordgames.gr
|
||||
Yiannis Stergiou - ys.stergiou@gmail.com
|
||||
Markos Gogoulos - mgogoulos@gmail.com
|
||||
|
||||
Please see https://github.com/mediacms-io/mediacms/graphs/contributors for complete list of contributors to this repository!
|
||||
13
CODE_OF_CONDUCT.md
Normal file
13
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Contributor Code of Conduct
|
||||
|
||||
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
|
||||
|
||||
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
|
||||
|
||||
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at https://www.contributor-covenant.org/version/1/0/0/code-of-conduct.html
|
||||
441
DOCKER_RESTRUCTURE_SUMMARY.md
Normal file
441
DOCKER_RESTRUCTURE_SUMMARY.md
Normal file
@@ -0,0 +1,441 @@
|
||||
# MediaCMS Docker Restructure Summary - Version 7.3
|
||||
|
||||
## Overview
|
||||
|
||||
MediaCMS 7.3 introduces a complete Docker architecture restructure, moving from a monolithic supervisord-based setup to modern microservices with proper separation of concerns.
|
||||
|
||||
**⚠️ BREAKING CHANGES** - See [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md) for migration guide.
|
||||
|
||||
## Architecture Comparison
|
||||
|
||||
### Before (7.x) - Monolithic
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Single Container │
|
||||
│ ┌──────────┐ │
|
||||
│ │Supervisor│ │
|
||||
│ └────┬─────┘ │
|
||||
│ ├─── nginx (port 80) │
|
||||
│ ├─── uwsgi (Django) │
|
||||
│ ├─── celery beat │
|
||||
│ ├─── celery workers │
|
||||
│ └─── migrations │
|
||||
│ │
|
||||
│ Volumes: ./ mounted to container │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### After (7.3) - Microservices
|
||||
```
|
||||
┌────────┐ ┌─────┐ ┌───────────┐ ┌──────────┐
|
||||
│ nginx │→ │ web │ │celery_beat│ │ celery │
|
||||
│ │ │uwsgi│ │ │ │ workers │
|
||||
└────────┘ └─────┘ └───────────┘ └──────────┘
|
||||
│
|
||||
┌───────┴────────┐
|
||||
│ db │ redis │
|
||||
└───────┴────────┘
|
||||
|
||||
Volumes: Named volumes + custom/ bind mount
|
||||
```
|
||||
|
||||
## What Changed
|
||||
|
||||
### 1. Container Services
|
||||
|
||||
| Component | Before (7.x) | After (7.3) |
|
||||
|-----------|-------------|-------------|
|
||||
| **nginx** | Inside main container | Separate container |
|
||||
| **Django/uWSGI** | Inside main container | Dedicated `web` container |
|
||||
| **Celery Beat** | Inside main container | Dedicated container |
|
||||
| **Celery Workers** | Inside main container | Separate containers (short/long) |
|
||||
| **Migrations** | Via environment flag | Init container (runs once) |
|
||||
|
||||
### 2. Volume Strategy
|
||||
|
||||
| Data | Before (7.x) | After (7.3) |
|
||||
|------|-------------|-------------|
|
||||
| **Application code** | Bind mount `./` | **Built into image** |
|
||||
| **Media files** | `./media_files` | **Named volume** `media_files` |
|
||||
| **Static files** | `./static` | **Built into image** (collectstatic at build) |
|
||||
| **Logs** | `./logs` | **Named volume** `logs` |
|
||||
| **PostgreSQL** | `../postgres_data` | **Named volume** `postgres_data` |
|
||||
| **Custom config** | `cms/local_settings.py` | **Bind mount** `./custom/` |
|
||||
|
||||
### 3. Removed Components
|
||||
|
||||
- ❌ supervisord and all supervisord configs
|
||||
- ❌ docker-entrypoint.sh (permission fixing script)
|
||||
- ❌ `ENABLE_*` environment variables
|
||||
- ❌ Runtime collectstatic
|
||||
- ❌ nginx from base image
|
||||
|
||||
### 4. New Components
|
||||
|
||||
- ✅ `custom/` directory for user customizations
|
||||
- ✅ Multi-stage Dockerfile (base, web, worker, worker-full)
|
||||
- ✅ Separate nginx image (`Dockerfile.nginx`)
|
||||
- ✅ Build-time collectstatic
|
||||
- ✅ USER www-data (non-root containers)
|
||||
- ✅ Health checks for all services
|
||||
- ✅ Makefile with common tasks
|
||||
|
||||
## Key Improvements
|
||||
|
||||
### Security
|
||||
- ✅ Containers run as `www-data` (UID 33), not root
|
||||
- ✅ Read-only mounts where possible
|
||||
- ✅ Smaller attack surface per container
|
||||
- ✅ No privilege escalation needed
|
||||
|
||||
### Performance
|
||||
- ✅ Named volumes have better I/O than bind mounts
|
||||
- ✅ Static files built into image (no runtime collection)
|
||||
- ✅ Faster container startups
|
||||
- ✅ No chown on millions of files at startup
|
||||
|
||||
### Scalability
|
||||
- ✅ Scale web and workers independently
|
||||
- ✅ Ready for load balancing
|
||||
- ✅ Can use Docker Swarm or Kubernetes
|
||||
- ✅ Horizontal scaling: `docker compose scale celery_short=3`
|
||||
|
||||
### Maintainability
|
||||
- ✅ One process per container (proper separation)
|
||||
- ✅ Clear service dependencies
|
||||
- ✅ Standard Docker patterns
|
||||
- ✅ Easier debugging (service-specific logs)
|
||||
- ✅ Immutable images
|
||||
|
||||
### Developer Experience
|
||||
- ✅ Separate dev compose with hot reload
|
||||
- ✅ `custom/` directory for all customizations
|
||||
- ✅ Clear documentation and examples
|
||||
- ✅ Makefile targets for common tasks
|
||||
|
||||
## New Customization System
|
||||
|
||||
### The `custom/` Directory
|
||||
|
||||
All user customizations now go in a dedicated directory:
|
||||
|
||||
```
|
||||
custom/
|
||||
├── README.md # Full documentation
|
||||
├── local_settings.py.example # Template file
|
||||
├── local_settings.py # Your Django settings (gitignored)
|
||||
└── static/
|
||||
├── images/ # Custom logos (gitignored)
|
||||
│ └── logo_dark.png
|
||||
└── css/ # Custom CSS (gitignored)
|
||||
└── custom.css
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Clear separation from core code
|
||||
- Works out-of-box (empty directory is fine)
|
||||
- Gitignored customizations
|
||||
- Well documented with examples
|
||||
|
||||
See [`custom/README.md`](./custom/README.md) for usage guide.
|
||||
|
||||
## Docker Images
|
||||
|
||||
### Images to Build
|
||||
|
||||
```bash
|
||||
# Web image (Django + uWSGI)
|
||||
docker build --target web -t mediacms/mediacms:7.3 .
|
||||
|
||||
# Worker image (Celery)
|
||||
docker build --target worker -t mediacms/mediacms-worker:7.3 .
|
||||
|
||||
# Worker-full image (Celery with extra codecs)
|
||||
docker build --target worker-full -t mediacms/mediacms-worker:7.3-full .
|
||||
|
||||
# Nginx image
|
||||
docker build -f Dockerfile.nginx -t mediacms/mediacms-nginx:7.3 .
|
||||
```
|
||||
|
||||
### Image Sizes
|
||||
|
||||
| Image | Approximate Size |
|
||||
|-------|-----------------|
|
||||
| mediacms:7.3 | ~800MB |
|
||||
| mediacms-worker:7.3 | ~800MB |
|
||||
| mediacms-worker:7.3-full | ~1.2GB |
|
||||
| mediacms-nginx:7.3 | ~50MB |
|
||||
|
||||
## Deployment Scenarios
|
||||
|
||||
### 1. Development
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose-dev.yaml up
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- File mounts for live editing
|
||||
- Django runserver with DEBUG=True
|
||||
- Frontend hot reload
|
||||
- Immediate code changes
|
||||
|
||||
### 2. Production (HTTP)
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Immutable images
|
||||
- Named volumes for data
|
||||
- Production-ready
|
||||
- Port 80
|
||||
|
||||
### 3. Production (HTTPS with Let's Encrypt)
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yaml -f docker-compose-cert.yaml up -d
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- Automatic SSL certificates
|
||||
- Auto-renewal
|
||||
- nginx-proxy + acme-companion
|
||||
- Production-ready
|
||||
|
||||
## Minimal Deployment (No Code Required!)
|
||||
|
||||
**Version 7.3 requires ONLY:**
|
||||
|
||||
1. ✅ `docker-compose.yaml` file
|
||||
2. ✅ Docker images (from Docker Hub)
|
||||
3. ⚠️ `custom/` directory (optional, only if customizing)
|
||||
|
||||
**No git repo needed!** Download docker-compose.yaml from release/docs and start.
|
||||
|
||||
## Migration Requirements
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
⚠️ **Not backward compatible** - Manual migration required
|
||||
|
||||
**What needs migration:**
|
||||
1. ✅ PostgreSQL database (dump and restore)
|
||||
2. ✅ Media files (copy to named volume)
|
||||
3. ✅ Custom settings → `custom/local_settings.py` (if you had them)
|
||||
4. ✅ Custom logos/CSS → `custom/static/` (if you had them)
|
||||
5. ⚠️ Backup scripts (new volume paths)
|
||||
6. ⚠️ Monitoring (new container names)
|
||||
|
||||
### Migration Steps
|
||||
|
||||
See [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md) for complete guide.
|
||||
|
||||
**Quick overview:**
|
||||
```bash
|
||||
# 1. Backup
|
||||
docker compose exec db pg_dump -U mediacms mediacms > backup.sql
|
||||
tar -czf media_backup.tar.gz media_files/
|
||||
cp docker-compose.yaml docker-compose.yaml.old
|
||||
|
||||
# 2. Download new docker-compose.yaml
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
|
||||
# 3. Create custom/ if needed
|
||||
mkdir -p custom/static/{images,css}
|
||||
# Copy your old settings/logos if you had them
|
||||
|
||||
# 4. Pull images and start
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
|
||||
# 5. Restore data
|
||||
cat backup.sql | docker compose exec -T db psql -U mediacms mediacms
|
||||
# (See full guide for media migration)
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### Created/Reorganized
|
||||
|
||||
```
|
||||
├── Dockerfile # Multi-stage (base, web, worker)
|
||||
├── Dockerfile.nginx # Nginx image
|
||||
├── docker-compose.yaml # Production
|
||||
├── docker-compose-cert.yaml # Production + HTTPS
|
||||
├── docker-compose-dev.yaml # Development
|
||||
├── Makefile # Common tasks
|
||||
├── custom/ # User customizations
|
||||
│ ├── README.md
|
||||
│ ├── local_settings.py.example
|
||||
│ └── static/
|
||||
├── config/
|
||||
│ ├── imagemagick/policy.xml
|
||||
│ ├── nginx/
|
||||
│ │ ├── nginx.conf
|
||||
│ │ └── site.conf
|
||||
│ ├── nginx-proxy/
|
||||
│ │ └── client_max_body_size.conf
|
||||
│ └── uwsgi/
|
||||
│ └── uwsgi.ini
|
||||
└── scripts/
|
||||
└── run-migrations.sh
|
||||
```
|
||||
|
||||
## Makefile Targets
|
||||
|
||||
New Makefile with common operations:
|
||||
|
||||
```bash
|
||||
make backup-db # PostgreSQL dump with timestamp
|
||||
make admin-shell # Quick Django shell access
|
||||
make build-frontend # Rebuild frontend assets
|
||||
make test # Run test suite
|
||||
```
|
||||
|
||||
## Rollback Strategy
|
||||
|
||||
If migration fails:
|
||||
|
||||
```bash
|
||||
# 1. Stop new version
|
||||
docker compose down
|
||||
|
||||
# 2. Checkout old version
|
||||
git checkout main
|
||||
|
||||
# 3. Restore old compose
|
||||
git checkout main docker-compose.yaml
|
||||
|
||||
# 4. Restore data from backups
|
||||
# (See UPGRADE_TO_7.3.md for details)
|
||||
|
||||
# 5. Start old version
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
Before production deployment:
|
||||
|
||||
- [ ] Migrations run successfully
|
||||
- [ ] Static files load correctly
|
||||
- [ ] Media files upload/download work
|
||||
- [ ] Video transcoding works (check celery_long logs)
|
||||
- [ ] Admin panel accessible
|
||||
- [ ] Custom settings loaded (if using custom/)
|
||||
- [ ] Database persists across restarts
|
||||
- [ ] Media persists across restarts
|
||||
- [ ] Logs accessible via `docker compose logs`
|
||||
- [ ] Health checks pass: `docker compose ps`
|
||||
|
||||
## Common Post-Upgrade Tasks
|
||||
|
||||
### View Logs
|
||||
```bash
|
||||
# Before: tail -f logs/uwsgi.log
|
||||
# After:
|
||||
docker compose logs -f web
|
||||
docker compose logs -f celery_long
|
||||
```
|
||||
|
||||
### Access Shell
|
||||
```bash
|
||||
# Before: docker exec -it <container> bash
|
||||
# After:
|
||||
make admin-shell
|
||||
# Or: docker compose exec web bash
|
||||
```
|
||||
|
||||
### Restart Service
|
||||
```bash
|
||||
# Before: docker restart <container>
|
||||
# After:
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
### Scale Workers
|
||||
```bash
|
||||
# New capability:
|
||||
docker compose up -d --scale celery_short=3 --scale celery_long=2
|
||||
```
|
||||
|
||||
### Database Backup
|
||||
```bash
|
||||
# Before: Custom script
|
||||
# After:
|
||||
make backup-db
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Startup Time
|
||||
- **Before**: Slower (chown on all files)
|
||||
- **After**: Faster (no permission fixing)
|
||||
|
||||
### I/O Performance
|
||||
- **Before**: Bind mount overhead
|
||||
- **After**: Named volumes (better performance)
|
||||
|
||||
### Memory Usage
|
||||
- **Before**: Single large container
|
||||
- **After**: Multiple smaller containers (better resource allocation)
|
||||
|
||||
## New Volume Management
|
||||
|
||||
### List Volumes
|
||||
```bash
|
||||
docker volume ls | grep mediacms
|
||||
```
|
||||
|
||||
### Inspect Volume
|
||||
```bash
|
||||
docker volume inspect mediacms_media_files
|
||||
```
|
||||
|
||||
### Backup Volume
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v mediacms_media_files:/data:ro \
|
||||
-v $(pwd):/backup \
|
||||
alpine tar czf /backup/media_backup.tar.gz -C /data .
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Upgrade Guide**: [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md)
|
||||
- **Customization**: [`custom/README.md`](./custom/README.md)
|
||||
- **Admin Docs**: `docs/admins_docs.md`
|
||||
|
||||
## Timeline Estimates
|
||||
|
||||
| Instance Size | Expected Migration Time |
|
||||
|---------------|------------------------|
|
||||
| Small (<100 videos) | 30-60 minutes |
|
||||
| Medium (100-1000 videos) | 1-3 hours |
|
||||
| Large (>1000 videos) | 3-8 hours |
|
||||
|
||||
**Plan accordingly and schedule during low-traffic periods!**
|
||||
|
||||
## Getting Help
|
||||
|
||||
1. Read [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md) thoroughly
|
||||
2. Check [`custom/README.md`](./custom/README.md) for customization
|
||||
3. Search GitHub Issues
|
||||
4. Test in staging first
|
||||
5. Keep backups for at least 1 week post-upgrade
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Read [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md)
|
||||
2. ✅ Test in development: `docker compose -f docker-compose-dev.yaml up`
|
||||
3. ✅ Backup production data
|
||||
4. ✅ Test migration in staging
|
||||
5. ✅ Plan maintenance window
|
||||
6. ✅ Execute migration
|
||||
7. ✅ Monitor for 24-48 hours
|
||||
|
||||
---
|
||||
|
||||
**Ready to upgrade?** Start with: [`UPGRADE_TO_7.3.md`](./UPGRADE_TO_7.3.md)
|
||||
126
Dockerfile
Normal file
126
Dockerfile
Normal file
@@ -0,0 +1,126 @@
|
||||
FROM python:3.13.5-slim-bookworm AS build-image
|
||||
|
||||
RUN apt-get update -y && \
|
||||
apt-get install -y --no-install-recommends wget xz-utils unzip && \
|
||||
rm -rf /var/lib/apt/lists/* && \
|
||||
apt-get purge --auto-remove && \
|
||||
apt-get clean
|
||||
|
||||
RUN wget -q https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz
|
||||
|
||||
RUN mkdir -p ffmpeg-tmp && \
|
||||
tar -xf ffmpeg-release-amd64-static.tar.xz --strip-components 1 -C ffmpeg-tmp && \
|
||||
cp -v ffmpeg-tmp/ffmpeg ffmpeg-tmp/ffprobe ffmpeg-tmp/qt-faststart /usr/local/bin && \
|
||||
rm -rf ffmpeg-tmp ffmpeg-release-amd64-static.tar.xz
|
||||
|
||||
RUN mkdir -p /home/mediacms.io/bento4 && \
|
||||
wget -q http://zebulon.bok.net/Bento4/binaries/Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip && \
|
||||
unzip Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip -d /home/mediacms.io/bento4 && \
|
||||
mv /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux/* /home/mediacms.io/bento4/ && \
|
||||
rm -rf /home/mediacms.io/bento4/Bento4-SDK-1-6-0-637.x86_64-unknown-linux && \
|
||||
rm -rf /home/mediacms.io/bento4/docs && \
|
||||
rm Bento4-SDK-1-6-0-637.x86_64-unknown-linux.zip
|
||||
|
||||
############ BASE RUNTIME IMAGE ############
|
||||
FROM python:3.13.5-slim-bookworm AS base
|
||||
|
||||
LABEL org.opencontainers.image.version="7.3"
|
||||
LABEL org.opencontainers.image.title="MediaCMS"
|
||||
LABEL org.opencontainers.image.description="Modern, scalable and open source video platform"
|
||||
|
||||
SHELL ["/bin/bash", "-c"]
|
||||
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
CELERY_APP='cms' \
|
||||
VIRTUAL_ENV=/home/mediacms.io \
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
RUN apt-get update -y && \
|
||||
apt-get -y upgrade && \
|
||||
apt-get install --no-install-recommends -y \
|
||||
imagemagick \
|
||||
procps \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
zlib1g-dev \
|
||||
zlib1g \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libxmlsec1-openssl \
|
||||
libpq-dev \
|
||||
gosu \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /home/mediacms.io/mediacms/{logs,media_files,static} && \
|
||||
cd /home/mediacms.io && \
|
||||
python3 -m venv $VIRTUAL_ENV
|
||||
|
||||
COPY requirements.txt requirements-dev.txt ./
|
||||
|
||||
ARG DEVELOPMENT_MODE=False
|
||||
RUN pip install --no-cache-dir uv && \
|
||||
uv pip install --no-binary lxml --no-binary xmlsec -r requirements.txt && \
|
||||
if [ "$DEVELOPMENT_MODE" = "True" ]; then \
|
||||
echo "Installing development dependencies..." && \
|
||||
uv pip install -r requirements-dev.txt; \
|
||||
fi && \
|
||||
apt-get purge -y --auto-remove \
|
||||
build-essential \
|
||||
pkg-config \
|
||||
libxml2-dev \
|
||||
libxmlsec1-dev \
|
||||
libpq-dev
|
||||
|
||||
COPY --from=build-image /usr/local/bin/ffmpeg /usr/local/bin/ffmpeg
|
||||
COPY --from=build-image /usr/local/bin/ffprobe /usr/local/bin/ffprobe
|
||||
COPY --from=build-image /usr/local/bin/qt-faststart /usr/local/bin/qt-faststart
|
||||
COPY --from=build-image /home/mediacms.io/bento4 /home/mediacms.io/bento4
|
||||
|
||||
COPY --chown=www-data:www-data . /home/mediacms.io/mediacms
|
||||
WORKDIR /home/mediacms.io/mediacms
|
||||
|
||||
# Copy imagemagick policy for sprite thumbnail generation
|
||||
COPY config/imagemagick/policy.xml /etc/ImageMagick-6/policy.xml
|
||||
|
||||
# Create www-data user directories and set permissions
|
||||
RUN mkdir -p /var/run/mediacms && \
|
||||
chown -R www-data:www-data /home/mediacms.io/mediacms/logs \
|
||||
/home/mediacms.io/mediacms/media_files \
|
||||
/home/mediacms.io/mediacms/static \
|
||||
/var/run/mediacms
|
||||
|
||||
# Collect static files during build
|
||||
RUN python manage.py collectstatic --noinput && \
|
||||
chown -R www-data:www-data /home/mediacms.io/mediacms/static
|
||||
|
||||
# Run container as www-data user
|
||||
USER www-data
|
||||
|
||||
############ WEB IMAGE (Django/uWSGI) ############
|
||||
FROM base AS web
|
||||
|
||||
# Install uWSGI
|
||||
RUN uv pip install uwsgi
|
||||
|
||||
# Copy uWSGI configuration
|
||||
COPY config/uwsgi/uwsgi.ini /home/mediacms.io/mediacms/uwsgi.ini
|
||||
|
||||
EXPOSE 9000
|
||||
|
||||
CMD ["/home/mediacms.io/bin/uwsgi", "--ini", "/home/mediacms.io/mediacms/uwsgi.ini"]
|
||||
|
||||
############ WORKER IMAGE (Celery) ############
|
||||
FROM base AS worker
|
||||
|
||||
# CMD will be overridden in docker-compose for different worker types
|
||||
|
||||
############ FULL WORKER IMAGE (Celery with extra codecs) ############
|
||||
FROM worker AS worker-full
|
||||
|
||||
COPY requirements-full.txt ./
|
||||
RUN mkdir -p /root/.cache/ && \
|
||||
chmod go+rwx /root/ && \
|
||||
chmod go+rwx /root/.cache/ && \
|
||||
uv pip install -r requirements-full.txt
|
||||
18
Dockerfile.nginx
Normal file
18
Dockerfile.nginx
Normal file
@@ -0,0 +1,18 @@
|
||||
FROM nginx:alpine
|
||||
|
||||
LABEL org.opencontainers.image.version="7.3"
|
||||
LABEL org.opencontainers.image.title="MediaCMS Nginx"
|
||||
LABEL org.opencontainers.image.description="Nginx server for MediaCMS"
|
||||
|
||||
# Copy nginx configurations
|
||||
COPY config/nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY config/nginx/site.conf /etc/nginx/conf.d/default.conf
|
||||
COPY config/nginx/uwsgi_params /etc/nginx/uwsgi_params
|
||||
|
||||
# Create directories for static and media files (will be volumes)
|
||||
RUN mkdir -p /var/www/media /var/www/static && \
|
||||
chown -R nginx:nginx /var/www
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
23
HISTORY.md
Normal file
23
HISTORY.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# History
|
||||
|
||||
## 3.0.0
|
||||
|
||||
### Features
|
||||
- Updates Python/Django requirements and Dockerfile to use latest 3.11 Python - https://github.com/mediacms-io/mediacms/pull/826/files. This update requires some manual steps, for existing (not new) installations. Check the update section under the [Admin docs](https://github.com/mediacms-io/mediacms/blob/main/docs/admins_docs.md#2-server-installation), either for single server or for Docker Compose installations
|
||||
- Upgrade postgres on Docker Compose - https://github.com/mediacms-io/mediacms/pull/749
|
||||
|
||||
### Fixes
|
||||
- video player options for HLS - https://github.com/mediacms-io/mediacms/pull/832
|
||||
- AVI videos not correctly recognised as videos - https://github.com/mediacms-io/mediacms/pull/833
|
||||
|
||||
## 2.1.0
|
||||
|
||||
### Fixes
|
||||
- Increase uwsgi buffer-size parameter. This prevents an error by uwsgi with large headers - [#5b60](https://github.com/mediacms-io/mediacms/commit/5b601698a41ad97f08c1830e14b1c18f73ab8315)
|
||||
- Fix issues with comments. These were not reported on the tracker but it is certain that they would not show comments on media files (non videos but also videos). Unfortunately this reverts work done with Timestamps on comments + Mentions on comments, more on PR [#802](https://github.com/mediacms-io/mediacms/pull/802)
|
||||
|
||||
### Features
|
||||
- Allow tags to contains other characters too, not only English alphabet ones [#801](https://github.com/mediacms-io/mediacms/pull/801)
|
||||
- Add simple cookie consent code [#799](https://github.com/mediacms-io/mediacms/pull/799)
|
||||
- Allow password reset & email verify pages on global login required [#790](https://github.com/mediacms-io/mediacms/pull/790)
|
||||
- Add api_url field to search api [#692](https://github.com/mediacms-io/mediacms/pull/692)
|
||||
32
Makefile
Normal file
32
Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
.PHONY: admin-shell build-frontend backup-db
|
||||
|
||||
admin-shell:
|
||||
@container_id=$$(docker compose ps -q web); \
|
||||
if [ -z "$$container_id" ]; then \
|
||||
echo "Web container not found"; \
|
||||
exit 1; \
|
||||
else \
|
||||
docker exec -it $$container_id /bin/bash; \
|
||||
fi
|
||||
|
||||
build-frontend:
|
||||
docker compose -f docker-compose-dev.yaml exec frontend npm run dist
|
||||
cp -r frontend/dist/static/* static/
|
||||
docker compose -f docker-compose-dev.yaml restart web
|
||||
|
||||
test:
|
||||
docker compose -f docker-compose-dev.yaml exec --env TESTING=True -T web pytest
|
||||
|
||||
backup-db:
|
||||
@echo "Creating PostgreSQL database dump..."
|
||||
@mkdir -p backups
|
||||
@timestamp=$$(date +%Y%m%d_%H%M%S); \
|
||||
dump_file="backups/mediacms_dump_$${timestamp}.sql"; \
|
||||
docker compose exec -T db pg_dump -U mediacms -d mediacms > "$${dump_file}"; \
|
||||
if [ $$? -eq 0 ]; then \
|
||||
echo "Database dump created successfully: $${dump_file}"; \
|
||||
else \
|
||||
echo "Database dump failed"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
292
QUICKSTART.md
Normal file
292
QUICKSTART.md
Normal file
@@ -0,0 +1,292 @@
|
||||
# MediaCMS 7.3 - Quick Start
|
||||
|
||||
## Minimal Deployment (No Code Required!)
|
||||
|
||||
MediaCMS 7.3 can be deployed with **just 2 files**:
|
||||
|
||||
1. `docker-compose.yaml`
|
||||
2. `custom/` directory (optional)
|
||||
|
||||
**No git repo, no code checkout needed!** Everything runs from Docker images.
|
||||
|
||||
---
|
||||
|
||||
## Fresh Installation
|
||||
|
||||
### 1. Create deployment directory
|
||||
|
||||
```bash
|
||||
mkdir mediacms && cd mediacms
|
||||
```
|
||||
|
||||
### 2. Download docker-compose.yaml
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
```
|
||||
|
||||
Or with curl:
|
||||
```bash
|
||||
curl -O https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
```
|
||||
|
||||
### 3. Start MediaCMS
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 4. Access your site
|
||||
|
||||
- **Frontend**: http://localhost
|
||||
- **Admin**: http://localhost/admin
|
||||
- Username: `admin`
|
||||
- Password: Check logs for auto-generated password:
|
||||
```bash
|
||||
docker compose logs migrations | grep "password:"
|
||||
```
|
||||
|
||||
**That's it!** 🎉
|
||||
|
||||
---
|
||||
|
||||
## Optional: Customization
|
||||
|
||||
### Add Custom Settings
|
||||
|
||||
```bash
|
||||
# 1. Create custom directory
|
||||
mkdir -p custom/static/{images,css}
|
||||
|
||||
# 2. Download example template
|
||||
wget -O custom/local_settings.py.example \
|
||||
https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/custom/local_settings.py.example
|
||||
|
||||
# 3. Copy and edit
|
||||
cp custom/local_settings.py.example custom/local_settings.py
|
||||
nano custom/local_settings.py
|
||||
```
|
||||
|
||||
Example customizations:
|
||||
```python
|
||||
# custom/local_settings.py
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = ['media.example.com']
|
||||
PORTAL_NAME = "My Media Portal"
|
||||
```
|
||||
|
||||
### Add Custom Logo
|
||||
|
||||
```bash
|
||||
# 1. Copy your logo
|
||||
cp ~/my-logo.png custom/static/images/logo_dark.png
|
||||
|
||||
# 2. Reference in settings
|
||||
cat >> custom/local_settings.py <<EOF
|
||||
PORTAL_LOGO_DARK_PNG = "/custom/static/images/logo_dark.png"
|
||||
EOF
|
||||
|
||||
# 3. Restart (no rebuild needed!)
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
### Add Custom CSS
|
||||
|
||||
```bash
|
||||
# 1. Create CSS file
|
||||
cat > custom/static/css/custom.css <<EOF
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
EOF
|
||||
|
||||
# 2. Reference in settings
|
||||
cat >> custom/local_settings.py <<EOF
|
||||
EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
EOF
|
||||
|
||||
# 3. Restart (no rebuild needed!)
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
**Note**: Both settings AND static files only need restart - nginx serves custom/ files directly!
|
||||
|
||||
---
|
||||
|
||||
## HTTPS with Let's Encrypt
|
||||
|
||||
### 1. Download cert overlay
|
||||
|
||||
```bash
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose-cert.yaml
|
||||
```
|
||||
|
||||
### 2. Edit domains
|
||||
|
||||
```bash
|
||||
nano docker-compose-cert.yaml
|
||||
```
|
||||
|
||||
Change these lines:
|
||||
```yaml
|
||||
VIRTUAL_HOST: 'media.example.com' # Your domain
|
||||
LETSENCRYPT_HOST: 'media.example.com' # Your domain
|
||||
LETSENCRYPT_EMAIL: 'admin@example.com' # Your email
|
||||
```
|
||||
|
||||
### 3. Start with SSL
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.yaml -f docker-compose-cert.yaml up -d
|
||||
```
|
||||
|
||||
**SSL certificates are issued automatically!**
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
Your deployment directory:
|
||||
|
||||
```
|
||||
mediacms/
|
||||
├── docker-compose.yaml # Required
|
||||
├── docker-compose-cert.yaml # Optional (for HTTPS)
|
||||
└── custom/ # Optional (for customizations)
|
||||
├── local_settings.py # Django settings
|
||||
└── static/
|
||||
├── images/ # Custom logos
|
||||
└── css/ # Custom CSS
|
||||
```
|
||||
|
||||
**Named volumes** (managed by Docker):
|
||||
- `mediacms_postgres_data` - Database
|
||||
- `mediacms_media_files` - Uploaded media
|
||||
- `mediacms_static_files` - Static assets
|
||||
- `mediacms_logs` - Application logs
|
||||
|
||||
---
|
||||
|
||||
## Common Commands
|
||||
|
||||
### View logs
|
||||
```bash
|
||||
docker compose logs -f web
|
||||
docker compose logs -f celery_long
|
||||
```
|
||||
|
||||
### Access Django shell
|
||||
```bash
|
||||
docker compose exec web python manage.py shell
|
||||
```
|
||||
|
||||
### Create admin user
|
||||
```bash
|
||||
docker compose exec web python manage.py createsuperuser
|
||||
```
|
||||
|
||||
### Restart service
|
||||
```bash
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
### Stop everything
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
### Update to newer version
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backup
|
||||
|
||||
### Database backup
|
||||
```bash
|
||||
docker compose exec db pg_dump -U mediacms mediacms > backup_$(date +%Y%m%d).sql
|
||||
```
|
||||
|
||||
### Media files backup
|
||||
```bash
|
||||
docker run --rm \
|
||||
-v mediacms_media_files:/data:ro \
|
||||
-v $(pwd):/backup \
|
||||
alpine tar czf /backup/media_backup_$(date +%Y%m%d).tar.gz -C /data .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Upgrading from 7.x?
|
||||
|
||||
If you're upgrading from an older MediaCMS version, see:
|
||||
- **[UPGRADE_TO_7.3.md](./UPGRADE_TO_7.3.md)** - Complete migration guide
|
||||
- **[DOCKER_RESTRUCTURE_SUMMARY.md](./DOCKER_RESTRUCTURE_SUMMARY.md)** - What changed
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Customization**: Download [`custom/README.md`](https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/custom/README.md)
|
||||
- **Upgrade Guide**: [UPGRADE_TO_7.3.md](./UPGRADE_TO_7.3.md)
|
||||
- **Architecture**: [DOCKER_RESTRUCTURE_SUMMARY.md](./DOCKER_RESTRUCTURE_SUMMARY.md)
|
||||
- **Project Docs**: https://docs.mediacms.io
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Can't access the site?
|
||||
|
||||
Check services are running:
|
||||
```bash
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
All services should be "Up" or "Exited (0)" for migrations.
|
||||
|
||||
### Forgot admin password?
|
||||
|
||||
Check logs:
|
||||
```bash
|
||||
docker compose logs migrations | grep "password:"
|
||||
```
|
||||
|
||||
Or create new admin:
|
||||
```bash
|
||||
docker compose exec web python manage.py createsuperuser
|
||||
```
|
||||
|
||||
### Videos not encoding?
|
||||
|
||||
Check celery workers:
|
||||
```bash
|
||||
docker compose logs celery_long
|
||||
docker compose logs celery_short
|
||||
```
|
||||
|
||||
### Port 80 already in use?
|
||||
|
||||
Edit docker-compose.yaml to use different port:
|
||||
```yaml
|
||||
nginx:
|
||||
ports:
|
||||
- "8080:80" # Use port 8080 instead
|
||||
```
|
||||
|
||||
Then access at http://localhost:8080
|
||||
|
||||
---
|
||||
|
||||
## Support
|
||||
|
||||
- **Issues**: https://github.com/mediacms-io/mediacms/issues
|
||||
- **Discussions**: https://github.com/mediacms-io/mediacms/discussions
|
||||
- **Docs**: https://docs.mediacms.io
|
||||
|
||||
---
|
||||
|
||||
**🎉 Enjoy MediaCMS!**
|
||||
158
README.md
158
README.md
@@ -1,160 +1,136 @@
|
||||

|
||||
# MediaCMS
|
||||
|
||||
MediaCMS is a modern, fully featured open source video and media CMS. It is developed to meet the needs of modern web platforms for viewing and sharing media. It can be used to build a small to medium video and media portal within minutes.
|
||||
[](https://raw.githubusercontent.com/mediacms-io/mediacms/main/LICENSE.txt)
|
||||
[](https://github.com/mediacms-io/mediacms/releases/)
|
||||
[](https://hub.docker.com/r/mediacms/mediacms)
|
||||
|
||||
|
||||
|
||||
MediaCMS is a modern, fully featured open source video and media CMS. It is developed to meet the needs of modern web platforms for viewing and sharing media. It can be used to build a small to medium video and media portal within minutes.
|
||||
|
||||
It is built mostly using the modern stack Django + React and includes a REST API.
|
||||
|
||||
A demo is available at https://demo.mediacms.io
|
||||
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||
|
||||
Vanilla MediaCMS index page
|
||||
|
||||

|
||||
|
||||
Video page with player different options
|
||||
|
||||

|
||||
|
||||
Embed video page
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/index.jpg" width="340">
|
||||
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/video.jpg" width="340">
|
||||
<img src="https://raw.githubusercontent.com/mediacms-io/mediacms/main/docs/images/embed.jpg" width="340">
|
||||
</p>
|
||||
|
||||
## Features
|
||||
- **Complete control over your data**: host it yourself!
|
||||
- **Support for multiple publishing workflows**: public, private, unlisted and custom
|
||||
- **Modern technologies**: Django/Python/Celery, React.
|
||||
- **Support for multiple publishing workflows**: public, private, unlisted and custom
|
||||
- **Role-Based Access Control (RBAC)**: create RBAC categories and connect users to groups with view/edit access on their media
|
||||
- **Automatic transcription**: through integration with Whisper running locally
|
||||
- **Multiple media types support**: video, audio, image, pdf
|
||||
- **Multiple media classification options**: categories, tags and custom
|
||||
- **Multiple media sharing options**: social media share, videos embed code generation
|
||||
- **Video Trimmer**: trim video, replace, save as new or create segments
|
||||
- **SAML support**: with ability to add mappings to system roles and groups
|
||||
- **Easy media searching**: enriched with live search functionality
|
||||
- **Playlists for audio and video content**: create playlists, add and reorder content
|
||||
- **Responsive design**: including light and dark themes
|
||||
- **Advanced users management**: allow self registration, invite only, closed.
|
||||
- **Configurable actions**: allow download, add comments, add likes, dislikes, report media
|
||||
- **Configuration options**: change logos, fonts, styling, add more pages
|
||||
- **Enhanced video player**: customized video.js player with multiple resolution and playback speed options
|
||||
- **Multiple transcoding profiles**: sane defaults for multiple dimensions (240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9)
|
||||
- **Multiple transcoding profiles**: sane defaults for multiple dimensions (144p, 240p, 360p, 480p, 720p, 1080p) and multiple profiles (h264, h265, vp9)
|
||||
- **Adaptive video streaming**: possible through HLS protocol
|
||||
- **Subtitles/CC**: support for multilingual subtitle files
|
||||
- **Scalable transcoding**: transcoding through priorities. Experimental support for remote workers
|
||||
- **Chunked file uploads**: for pausable/resumable upload of content
|
||||
|
||||
- **REST API**: Documented through Swagger
|
||||
- **Translation**: Most of the CMS is translated to a number of languages
|
||||
|
||||
## Example cases
|
||||
|
||||
- **Schools, education.** Administrators and editors keep what content will be published, students are not distracted with advertisements and irrelevant content, plus they have the ability to select either to stream or download content.
|
||||
|
||||
- **Universities, schools, education.** Administrators and editors keep what content will be published, students are not distracted with advertisements and irrelevant content, plus they have the ability to select either to stream or download content.
|
||||
- **Organization sensitive content.** In cases where content is sensitive and cannot be uploaded to external sites.
|
||||
|
||||
- **Build a great community.** MediaCMS can be customized (URLs, logos, fonts, aesthetics) so that you create a highly customized video portal for your community!
|
||||
|
||||
- **Personal portal.** Organize, categorize and host your content the way you prefer.
|
||||
|
||||
|
||||
## Philosophy
|
||||
|
||||
We believe there's a need for quality open source web applications that can be used to build community portals and support collaboration.
|
||||
|
||||
We have three goals for MediaCMS: a) deliver all functionality one would expect from a modern system, b) allow for easy installation and maintenance, c) allow easy customization and addition of features.
|
||||
We believe there's a need for quality open source web applications that can be used to build community portals and support collaboration.
|
||||
We have three goals for MediaCMS: a) deliver all functionality one would expect from a modern system, b) allow for easy installation and maintenance, c) allow easy customization and addition of features.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt).
|
||||
Copyright Markos Gogoulos and Yiannis Stergiou
|
||||
MediaCMS is released under [GNU Affero General Public License v3.0 license](LICENSE.txt).
|
||||
Copyright Markos Gogoulos.
|
||||
|
||||
|
||||
## Support and paid services
|
||||
|
||||
We provide custom installations, development of extra functionality, migration from existing systems, integrations with legacy systems, training and support. Contact us at info@mediacms.io for more information.
|
||||
|
||||
### Commercial Hostings
|
||||
**Elestio**
|
||||
|
||||
You can deploy MediaCMS on Elestio using one-click deployment. Elestio supports MediaCMS by providing revenue share so go ahead and click below to deploy and use MediaCMS.
|
||||
|
||||
## Hardware dependencies
|
||||
[](https://elest.io/open-source/mediacms)
|
||||
|
||||
For a small to medium installation, with a few hours of video uploaded daily, and a few hundreds of active daily users viewing content, 4GB Ram / 2-4 CPUs as minimum is ok. For a larger installation with many hours of video uploaded daily, consider adding more CPUs and more Ram.
|
||||
## Hardware considerations
|
||||
|
||||
For a small to medium installation, with a few hours of video uploaded daily, and a few hundreds of active daily users viewing content, 4GB Ram / 2-4 CPUs as minimum is ok. For a larger installation with many hours of video uploaded daily, consider adding more CPUs and more Ram.
|
||||
|
||||
In terms of disk space, think of what the needs will be. A general rule is to multiply by three the size of the expected uploaded videos (since the system keeps original versions, encoded versions plus HLS), so if you receive 1G of videos daily and maintain all of them, you should consider a 1T disk across a year (1G * 3 * 365).
|
||||
|
||||
In order to support automatic transcriptions through Whisper, consider more CPUs.
|
||||
|
||||
## Install
|
||||
## Installation / Maintanance
|
||||
|
||||
The core dependencies are Python3, Django3, Celery, PostgreSQL, Redis, ffmpeg. Any system that can have these dependencies installed, can run MediaCMS. But we strongly suggest installing on Linux Ubuntu 18 or 20 versions.
|
||||
There are two ways to run MediaCMS, through Docker Compose and through installing it on a server via an automation script that installs and configures all needed services. Find the related pages:
|
||||
|
||||
Installation on a Ubuntu 18 or 20 system with git utility installed should be completed in a few minutes with the following steps.
|
||||
Make sure you run it as user root, on a clear system, since the automatic script will install and configure the following services: Celery/PostgreSQL/Redis/Nginx and will override any existing settings.
|
||||
- [Single Server](docs/admins_docs.md#2-server-installation) page
|
||||
- [Docker Compose](docs/admins_docs.md#3-docker-installation) page
|
||||
|
||||
Automated script - to run on Ubuntu 18 or Ubuntu 20 flavors only!
|
||||
A complete guide can be found on the blog post [How to self-host and share your videos in 2021](https://medium.com/@MediaCMS.io/how-to-self-host-and-share-your-videos-in-2021-14067e3b291b).
|
||||
|
||||
```bash
|
||||
mkdir /home/mediacms.io && cd /home/mediacms.io/
|
||||
git clone https://github.com/mediacms-io/mediacms
|
||||
cd /home/mediacms.io/mediacms/ && bash ./install.sh
|
||||
```
|
||||
## Documentation
|
||||
|
||||
The script will ask if you have a URL where you want to deploy MediaCMS, otherwise it will use localhost. If you provide a URL, it will use Let's Encrypt service to install a valid ssl certificate.
|
||||
|
||||
|
||||
## Update
|
||||
|
||||
If you've used the above way to install MediaCMS, update with the following:
|
||||
|
||||
```bash
|
||||
cd /home/mediacms.io/mediacms # enter mediacms directory
|
||||
source /home/mediacms.io/bin/activate # use virtualenv
|
||||
git pull # update code
|
||||
python manage.py migrate # run Django migrations
|
||||
sudo systemctl restart mediacms celery_long celery_short # restart services
|
||||
```
|
||||
|
||||
|
||||
## Configure
|
||||
|
||||
Several options are available on cms/settings.py, most of the things that are allowed or should be disallowed are described there. It is advisable to override any of them by adding it to cms/local_settings.py. All configuration options will be documented gradually on the [Configuration](docs/Configuration.md) page.
|
||||
|
||||
## Authors
|
||||
MediaCMS is developed by Yiannis Stergiou and Markos Gogoulos. We are Wordgames - https://wordgames.gr
|
||||
* [Users documentation](docs/user_docs.md) page
|
||||
* [Administrators documentation](docs/admins_docs.md) page
|
||||
* [Developers documentation](docs/developers_docs.md) page
|
||||
* [Configuration](docs/admins_docs.md#5-configuration) page
|
||||
* [Transcoding](docs/transcoding.md) page
|
||||
* [Developer Experience](docs/dev_exp.md) page
|
||||
* [Media Permissions](docs/media_permissions.md) page
|
||||
|
||||
|
||||
## Technology
|
||||
This software uses the following list of awesome technologies:
|
||||
- Python
|
||||
- Django
|
||||
- Django Rest Framework
|
||||
- Celery
|
||||
- PostgreSQL
|
||||
- Redis
|
||||
- Nginx
|
||||
- uWSGI
|
||||
- React
|
||||
- Fine Uploader
|
||||
- video.js
|
||||
- FFMPEG
|
||||
- Bento4
|
||||
|
||||
This software uses the following list of awesome technologies: Python, Django, Django Rest Framework, Celery, PostgreSQL, Redis, Nginx, uWSGI, React, Fine Uploader, video.js, FFMPEG, Bento4
|
||||
|
||||
|
||||
## Who is using it
|
||||
|
||||
- **EngageMedia** non-profit media, technology and culture organization - https://video.engagemedia.org
|
||||
|
||||
- **Multiple Universities** for hosting educational videos
|
||||
- **Cinemata** non-profit media, technology and culture organization - https://cinemata.org
|
||||
- **Critical Commons** public media archive and fair use advocacy network - https://criticalcommons.org
|
||||
|
||||
- **Heritales** International Heritage Film Festival - https://stage.heritales.org
|
||||
- **American Association of Gynecologic Laparoscopists** - https://surgeryu.org/
|
||||
|
||||
|
||||
## Thanks To
|
||||
## How to contribute
|
||||
|
||||
- **Anna Helme**, for such a great partnership all these years!
|
||||
|
||||
- **Steve Anderson**, for trusting us and helping the Wordgames team make this real.
|
||||
|
||||
- **Andrew Lowenthal, King Catoy, Rezwan Islam** and the rest of the great team of [Engage Media](https://engagemedia.org).
|
||||
|
||||
- **Ioannis Korovesis, Ioannis Maistros, Diomidis Spinellis and Theodoros Karounos**, for their mentorship all these years, their contribution to science and the promotion of open source and free software technologies.
|
||||
|
||||
- **Antonis Ikonomou**, for hosting us on the excellent [Innovathens](https://www.innovathens.gr) space.
|
||||
|
||||
- **Werner Robitza**, for helping us with ffmpeg related stuff.
|
||||
If you like the project, here's a few things you can do
|
||||
- Hire us, for custom installations, training, support, maintenance work
|
||||
- Suggest us to others that are interested to hire us
|
||||
- Write a blog post/article about MediaCMS
|
||||
- Share on social media about the project
|
||||
- Open issues, participate on [discussions](https://github.com/mediacms-io/mediacms/discussions), report bugs, suggest ideas
|
||||
- [Show and tell](https://github.com/mediacms-io/mediacms/discussions/categories/show-and-tell) how you are using the project
|
||||
- Star the project
|
||||
- Add functionality, work on a PR, fix an issue!
|
||||
|
||||
|
||||
## Contact
|
||||
|
||||
info@mediacms.io
|
||||
|
||||
477
UPGRADE_TO_7.3.md
Normal file
477
UPGRADE_TO_7.3.md
Normal file
@@ -0,0 +1,477 @@
|
||||
# Upgrade Guide: MediaCMS 7.x to 7.3
|
||||
|
||||
**IMPORTANT: This is a major architectural change. Read this entire guide before upgrading.**
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Fresh Install (Not Upgrading)?
|
||||
|
||||
If you're starting fresh with 7.3, you don't need this guide!
|
||||
|
||||
**All you need:**
|
||||
```bash
|
||||
# 1. Download docker-compose.yaml
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
|
||||
# 2. Start (creates everything automatically)
|
||||
docker compose up -d
|
||||
|
||||
# 3. Done! Visit http://localhost
|
||||
```
|
||||
|
||||
**Optional: Add customizations**
|
||||
```bash
|
||||
# Create custom/ directory
|
||||
mkdir -p custom/static/{images,css}
|
||||
|
||||
# Download example settings
|
||||
wget -O custom/local_settings.py.example \
|
||||
https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/custom/local_settings.py.example
|
||||
|
||||
# Edit and use
|
||||
cp custom/local_settings.py.example custom/local_settings.py
|
||||
nano custom/local_settings.py
|
||||
|
||||
# Restart
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
See [`custom/README.md`](https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/custom/README.md) for customization options.
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Upgrading from 7.x? Continue reading...
|
||||
|
||||
## What Changed in 7.3
|
||||
|
||||
### Architecture Changes
|
||||
- **Before**: Monolithic container (supervisor + nginx + uwsgi + celery in one)
|
||||
- **After**: Microservices (separate nginx, web, celery_beat, celery_short, celery_long containers)
|
||||
|
||||
### Volume Strategy Changes
|
||||
- **Before**: Entire project directory mounted (`./:/home/mediacms.io/mediacms/`)
|
||||
- **After**: Named volumes for data, bind mount only for `custom/` directory
|
||||
|
||||
### Specific Changes
|
||||
|
||||
| Component | Before (7.x) | After (7.3) |
|
||||
|-----------|-------------|-------------|
|
||||
| media_files | Bind mount `./media_files` | Named volume `media_files` |
|
||||
| static files | Bind mount `./static` | Named volume `static_files` (built into image) |
|
||||
| logs | Bind mount `./logs` | Named volume `logs` |
|
||||
| postgres_data | `../postgres_data` | Named volume `postgres_data` |
|
||||
| Custom config | `cms/local_settings.py` in mounted dir | `custom/local_settings.py` bind mount |
|
||||
| Static collection | Runtime (via entrypoint) | Build time (in Dockerfile) |
|
||||
| User | Root with gosu switch | www-data from start |
|
||||
|
||||
## What You Need for 7.3
|
||||
|
||||
**Minimal deployment - NO CODE REQUIRED:**
|
||||
|
||||
1. ✅ `docker-compose.yaml` (download from release or docs)
|
||||
2. ✅ Docker images (pulled from Docker Hub)
|
||||
3. ⚠️ `custom/` directory (only if you have customizations)
|
||||
|
||||
**That's it!** No git repo, no code checkout needed.
|
||||
|
||||
## Pre-Upgrade Checklist
|
||||
|
||||
### 1. Backup Everything
|
||||
|
||||
```bash
|
||||
# Stop services
|
||||
docker compose down
|
||||
|
||||
# Backup media files
|
||||
tar -czf backup_media_$(date +%Y%m%d).tar.gz media_files/
|
||||
|
||||
# Backup database
|
||||
docker compose up -d db
|
||||
docker compose exec db pg_dump -U mediacms mediacms > backup_db_$(date +%Y%m%d).sql
|
||||
docker compose down
|
||||
|
||||
# Backup logs (optional)
|
||||
tar -czf backup_logs_$(date +%Y%m%d).tar.gz logs/
|
||||
|
||||
# Backup local settings if you had them
|
||||
cp cms/local_settings.py backup_local_settings.py 2>/dev/null || echo "No local_settings.py found"
|
||||
|
||||
# Backup current docker-compose.yaml
|
||||
cp docker-compose.yaml docker-compose.yaml.old
|
||||
```
|
||||
|
||||
### 2. Document Current Setup
|
||||
|
||||
```bash
|
||||
# Save current docker-compose version
|
||||
git branch backup-pre-7.3-upgrade
|
||||
|
||||
# Document current state
|
||||
docker compose ps > pre_upgrade_state.txt
|
||||
docker compose config > pre_upgrade_config.yaml
|
||||
df -h > pre_upgrade_disk_usage.txt
|
||||
```
|
||||
|
||||
### 3. Check Disk Space
|
||||
|
||||
You'll need enough space for:
|
||||
- Existing data (media_files, postgres_data)
|
||||
- New Docker volumes (will copy data here)
|
||||
- Database dump
|
||||
|
||||
```bash
|
||||
du -sh media_files/ postgres_data/ logs/
|
||||
df -h .
|
||||
```
|
||||
|
||||
## Upgrade Methods
|
||||
|
||||
### Method 1: Clean Migration (Recommended)
|
||||
|
||||
This method migrates your data to the new volume structure.
|
||||
|
||||
#### Step 1: Get New docker-compose.yaml
|
||||
|
||||
**Option A: Download from release**
|
||||
```bash
|
||||
# Download docker-compose.yaml for 7.3
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
|
||||
# Or using curl
|
||||
curl -O https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose.yaml
|
||||
|
||||
# Optional: Download HTTPS version
|
||||
wget https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/docker-compose-cert.yaml
|
||||
```
|
||||
|
||||
**Option B: Copy from docs/release notes**
|
||||
- Copy the docker-compose.yaml content from release notes
|
||||
- Save as `docker-compose.yaml` in your deployment directory
|
||||
|
||||
#### Step 2: Prepare Custom Configuration (if needed)
|
||||
|
||||
```bash
|
||||
# Create custom directory structure (only if you need customizations)
|
||||
mkdir -p custom/static/{images,css}
|
||||
touch custom/static/{images,css}/.gitkeep
|
||||
|
||||
# If you had local_settings.py, create it in custom/
|
||||
if [ -f backup_local_settings.py ]; then
|
||||
# Copy your old settings
|
||||
cp backup_local_settings.py custom/local_settings.py
|
||||
echo "✓ Migrated local_settings.py"
|
||||
else
|
||||
# Download example template (optional)
|
||||
wget -O custom/local_settings.py.example \
|
||||
https://raw.githubusercontent.com/mediacms-io/mediacms/v7.3/custom/local_settings.py.example
|
||||
echo "Downloaded example template to custom/local_settings.py.example"
|
||||
fi
|
||||
|
||||
# Copy any custom logos/css you had
|
||||
# (adjust paths as needed for your old setup)
|
||||
# cp my-old-logo.png custom/static/images/logo_dark.png
|
||||
# cp my-custom.css custom/static/css/custom.css
|
||||
```
|
||||
|
||||
#### Step 3: Start New Stack (Without Data)
|
||||
|
||||
```bash
|
||||
# Pull new images
|
||||
docker compose pull
|
||||
|
||||
# Start database first
|
||||
docker compose up -d db redis
|
||||
|
||||
# Wait for DB to be ready
|
||||
sleep 10
|
||||
```
|
||||
|
||||
#### Step 4: Restore Database
|
||||
|
||||
```bash
|
||||
# Copy backup into container
|
||||
docker compose cp backup_db_*.sql db:/tmp/backup.sql
|
||||
|
||||
# Restore database
|
||||
docker compose exec db psql -U mediacms mediacms < /tmp/backup.sql
|
||||
|
||||
# Or from host:
|
||||
cat backup_db_*.sql | docker compose exec -T db psql -U mediacms mediacms
|
||||
```
|
||||
|
||||
#### Step 5: Restore Media Files
|
||||
|
||||
```bash
|
||||
# Start all services (will create volumes)
|
||||
docker compose up -d
|
||||
|
||||
# Find the volume name
|
||||
docker volume ls | grep media_files
|
||||
|
||||
# Copy media files to volume
|
||||
# Method A: Using a temporary container
|
||||
docker run --rm \
|
||||
-v $(pwd)/media_files:/source:ro \
|
||||
-v mediacms_media_files:/dest \
|
||||
alpine sh -c "cp -av /source/* /dest/"
|
||||
|
||||
# Method B: Using existing container
|
||||
docker compose exec web sh -c "exit" # Ensure web is running
|
||||
# Then copy from host
|
||||
tar -C media_files -cf - . | docker compose exec -T web tar -C /home/mediacms.io/mediacms/media_files -xf -
|
||||
```
|
||||
|
||||
#### Step 6: Verify and Test
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose logs -f web
|
||||
|
||||
# Verify media files are accessible
|
||||
docker compose exec web ls -la /home/mediacms.io/mediacms/media_files/
|
||||
|
||||
# Check database connection
|
||||
docker compose exec web python manage.py dbshell
|
||||
|
||||
# Access the site
|
||||
curl http://localhost
|
||||
|
||||
# Check admin panel
|
||||
# Visit http://localhost/admin
|
||||
```
|
||||
|
||||
### Method 2: In-Place Migration with Symlinks (Advanced)
|
||||
|
||||
**Warning**: This is more complex but avoids data copying.
|
||||
|
||||
#### Step 1: Keep Old Data Locations
|
||||
|
||||
```bash
|
||||
# Modify docker-compose.yaml to mount old locations temporarily
|
||||
# Add to appropriate services:
|
||||
volumes:
|
||||
- ./media_files:/home/mediacms.io/mediacms/media_files
|
||||
- ./logs:/home/mediacms.io/mediacms/logs
|
||||
# Instead of named volumes
|
||||
```
|
||||
|
||||
#### Step 2: Gradually Migrate
|
||||
|
||||
After confirming everything works:
|
||||
1. Copy data to named volumes
|
||||
2. Remove bind mounts
|
||||
3. Switch to named volumes
|
||||
|
||||
### Method 3: Fresh Install (If Possible)
|
||||
|
||||
If your MediaCMS instance is new or test:
|
||||
|
||||
```bash
|
||||
# Backup what you need
|
||||
# ...
|
||||
|
||||
# Clean slate
|
||||
docker compose down -v
|
||||
rm -rf media_files/ logs/ static/
|
||||
|
||||
# Fresh start
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Post-Upgrade Steps
|
||||
|
||||
### 1. Verify Everything Works
|
||||
|
||||
```bash
|
||||
# Check all services are running
|
||||
docker compose ps
|
||||
|
||||
# Should see: migrations (exited 0), web, nginx, celery_beat, celery_short, celery_long, db, redis
|
||||
|
||||
# Check logs for errors
|
||||
docker compose logs web
|
||||
docker compose logs nginx
|
||||
|
||||
# Test upload functionality
|
||||
# Test video encoding (check celery_long logs)
|
||||
# Test frontend
|
||||
```
|
||||
|
||||
### 2. Verify Media Files
|
||||
|
||||
```bash
|
||||
# Check media files are accessible
|
||||
docker compose exec web ls -lh /home/mediacms.io/mediacms/media_files/
|
||||
|
||||
# Check file counts match
|
||||
# Old: ls media_files/ | wc -l
|
||||
# New: docker compose exec web sh -c "ls /home/mediacms.io/mediacms/media_files/ | wc -l"
|
||||
```
|
||||
|
||||
### 3. Verify Database
|
||||
|
||||
```bash
|
||||
# Check users
|
||||
docker compose exec db psql -U mediacms mediacms -c "SELECT count(*) FROM users_user;"
|
||||
|
||||
# Check videos
|
||||
docker compose exec db psql -U mediacms mediacms -c "SELECT count(*) FROM files_media;"
|
||||
```
|
||||
|
||||
### 4. Update Backups
|
||||
|
||||
```bash
|
||||
# Update your backup scripts for new volume locations
|
||||
# Use: make backup-db (if Makefile target exists)
|
||||
# Or: docker compose exec db pg_dump ...
|
||||
```
|
||||
|
||||
## Rollback Procedure
|
||||
|
||||
If something goes wrong:
|
||||
|
||||
### Quick Rollback
|
||||
|
||||
```bash
|
||||
# Stop new version
|
||||
docker compose down
|
||||
|
||||
# Restore old docker-compose file
|
||||
mv docker-compose.yaml.old docker-compose.yaml
|
||||
|
||||
# Pull old images (if you had old image tags documented)
|
||||
docker compose pull
|
||||
|
||||
# Start old version
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Full Rollback with Data Restore
|
||||
|
||||
```bash
|
||||
# Stop everything
|
||||
docker compose down -v
|
||||
|
||||
# Restore old docker-compose
|
||||
mv docker-compose.yaml.old docker-compose.yaml
|
||||
|
||||
# Restore backups
|
||||
tar -xzf backup_media_*.tar.gz -C ./media_files
|
||||
cat backup_db_*.sql | docker compose exec -T db psql -U mediacms mediacms
|
||||
|
||||
# Start old version
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
## Common Issues & Solutions
|
||||
|
||||
### Issue: "Volume not found"
|
||||
|
||||
**Solution**: Volumes are created with project name prefix. Check:
|
||||
```bash
|
||||
docker volume ls
|
||||
# Look for: mediacms_media_files, mediacms_static_files, etc.
|
||||
```
|
||||
|
||||
### Issue: "Permission denied" on media files
|
||||
|
||||
**Solution**: Files must be owned by www-data (UID 33)
|
||||
```bash
|
||||
docker compose exec web chown -R www-data:www-data /home/mediacms.io/mediacms/media_files
|
||||
```
|
||||
|
||||
### Issue: Static files not loading
|
||||
|
||||
**Solution**: Rebuild image (collectstatic runs at build time)
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose build --no-cache web
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Issue: Database connection refused
|
||||
|
||||
**Solution**: Check database is healthy
|
||||
```bash
|
||||
docker compose logs db
|
||||
docker compose exec db pg_isready -U mediacms
|
||||
```
|
||||
|
||||
### Issue: Custom settings not loading
|
||||
|
||||
**Solution**: Check custom/local_settings.py exists and syntax
|
||||
```bash
|
||||
docker compose exec web cat /home/mediacms.io/mediacms/custom/local_settings.py
|
||||
docker compose exec web python -m py_compile /home/mediacms.io/mediacms/custom/local_settings.py
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### New Volume Performance
|
||||
|
||||
Named volumes are typically faster than bind mounts:
|
||||
- **Before**: Filesystem overhead on host
|
||||
- **After**: Direct container filesystem (better I/O)
|
||||
|
||||
### Monitoring Volume Usage
|
||||
|
||||
```bash
|
||||
# Check volume sizes
|
||||
docker system df -v
|
||||
|
||||
# Check specific volume
|
||||
docker volume inspect mediacms_media_files
|
||||
```
|
||||
|
||||
## New Backup Strategy
|
||||
|
||||
With named volumes, backups change:
|
||||
|
||||
```bash
|
||||
# Database backup
|
||||
docker compose exec db pg_dump -U mediacms mediacms > backup.sql
|
||||
|
||||
# Media files backup
|
||||
docker run --rm \
|
||||
-v mediacms_media_files:/data:ro \
|
||||
-v $(pwd):/backup \
|
||||
alpine tar czf /backup/media_backup_$(date +%Y%m%d).tar.gz -C /data .
|
||||
```
|
||||
|
||||
Or use the Makefile:
|
||||
```bash
|
||||
make backup-db
|
||||
```
|
||||
|
||||
## Getting Help
|
||||
|
||||
If you encounter issues:
|
||||
|
||||
1. **Check logs**: `docker compose logs <service>`
|
||||
2. **Check GitHub Issues**: Search for similar problems
|
||||
3. **Rollback**: Use the rollback procedure above
|
||||
4. **Report**: Open an issue with:
|
||||
- Your docker-compose.yaml
|
||||
- Output of `docker compose ps`
|
||||
- Relevant logs
|
||||
- Steps to reproduce
|
||||
|
||||
## Summary of Benefits
|
||||
|
||||
After upgrading to 7.3:
|
||||
|
||||
✅ **Better separation of concerns** - each service has one job
|
||||
✅ **Easier scaling** - scale web/workers independently
|
||||
✅ **Better security** - containers run as www-data, not root
|
||||
✅ **Faster deployments** - static files built into image
|
||||
✅ **Cleaner customization** - dedicated custom/ directory
|
||||
✅ **Easier SSL setup** - docker-compose-cert.yaml overlay
|
||||
✅ **Better volume management** - named volumes instead of bind mounts
|
||||
|
||||
## Timeline Recommendation
|
||||
|
||||
- **Small instance** (<100 videos): 30-60 minutes
|
||||
- **Medium instance** (100-1000 videos): 1-3 hours
|
||||
- **Large instance** (>1000 videos): Plan for several hours
|
||||
|
||||
Schedule during low-traffic period!
|
||||
@@ -4,7 +4,6 @@ from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-01 07:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
# Generated by Django 3.1.4 on 2020-12-01 07:12
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
@@ -35,8 +34,6 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="mediaaction",
|
||||
index=models.Index(
|
||||
fields=["session_key", "action"], name="actions_med_session_fac55a_idx"
|
||||
),
|
||||
index=models.Index(fields=["session_key", "action"], name="actions_med_session_fac55a_idx"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from django.db import models
|
||||
from users.models import User
|
||||
|
||||
from files.models import Media
|
||||
from users.models import User
|
||||
|
||||
USER_MEDIA_ACTIONS = (
|
||||
("like", "Like"),
|
||||
@@ -30,15 +31,11 @@ class MediaAction(models.Model):
|
||||
help_text="for not logged in users",
|
||||
)
|
||||
|
||||
action = models.CharField(
|
||||
max_length=20, choices=USER_MEDIA_ACTIONS, default="watch"
|
||||
)
|
||||
action = models.CharField(max_length=20, choices=USER_MEDIA_ACTIONS, default="watch")
|
||||
# keeps extra info, eg on report action, why it is reported
|
||||
extra_info = models.TextField(blank=True, null=True)
|
||||
|
||||
media = models.ForeignKey(
|
||||
Media, on_delete=models.CASCADE, related_name="mediaactions"
|
||||
)
|
||||
media = models.ForeignKey(Media, on_delete=models.CASCADE, related_name="mediaactions")
|
||||
action_date = models.DateTimeField(auto_now_add=True)
|
||||
remote_ip = models.CharField(max_length=40, blank=True, null=True)
|
||||
|
||||
|
||||
0
admin_customizations/admin.py
Normal file
0
admin_customizations/admin.py
Normal file
86
admin_customizations/apps.py
Normal file
86
admin_customizations/apps.py
Normal file
@@ -0,0 +1,86 @@
|
||||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
|
||||
|
||||
class AdminCustomizationsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'admin_customizations'
|
||||
|
||||
def ready(self):
|
||||
original_get_app_list = admin.AdminSite.get_app_list
|
||||
|
||||
def get_app_list(self, request, app_label=None):
|
||||
"""Custom get_app_list"""
|
||||
app_list = original_get_app_list(self, request, app_label)
|
||||
# To see the list:
|
||||
# print([a.get('app_label') for a in app_list])
|
||||
|
||||
email_model = None
|
||||
rbac_group_model = None
|
||||
identity_providers_user_log_model = None
|
||||
identity_providers_login_option = None
|
||||
auth_app = None
|
||||
rbac_app = None
|
||||
socialaccount_app = None
|
||||
|
||||
for app in app_list:
|
||||
if app['app_label'] == 'users':
|
||||
auth_app = app
|
||||
|
||||
elif app['app_label'] == 'account':
|
||||
for model in app['models']:
|
||||
if model['object_name'] == 'EmailAddress':
|
||||
email_model = model
|
||||
elif app['app_label'] == 'rbac':
|
||||
if not getattr(settings, 'USE_RBAC', False):
|
||||
continue
|
||||
rbac_app = app
|
||||
for model in app['models']:
|
||||
if model['object_name'] == 'RBACGroup':
|
||||
rbac_group_model = model
|
||||
elif app['app_label'] == 'identity_providers':
|
||||
if not getattr(settings, 'USE_IDENTITY_PROVIDERS', False):
|
||||
continue
|
||||
|
||||
models_to_check = list(app['models'])
|
||||
|
||||
for model in models_to_check:
|
||||
if model['object_name'] == 'IdentityProviderUserLog':
|
||||
identity_providers_user_log_model = model
|
||||
if model['object_name'] == 'LoginOption':
|
||||
identity_providers_login_option = model
|
||||
elif app['app_label'] == 'socialaccount':
|
||||
socialaccount_app = app
|
||||
|
||||
if email_model and auth_app:
|
||||
auth_app['models'].append(email_model)
|
||||
if rbac_group_model and rbac_app and auth_app:
|
||||
auth_app['models'].append(rbac_group_model)
|
||||
if identity_providers_login_option and socialaccount_app:
|
||||
socialaccount_app['models'].append(identity_providers_login_option)
|
||||
if identity_providers_user_log_model and socialaccount_app:
|
||||
socialaccount_app['models'].append(identity_providers_user_log_model)
|
||||
|
||||
# 2. don't include the following apps
|
||||
apps_to_hide = ['authtoken', 'auth', 'account', 'saml_auth', 'rbac']
|
||||
if not getattr(settings, 'USE_RBAC', False):
|
||||
apps_to_hide.append('rbac')
|
||||
if not getattr(settings, 'USE_IDENTITY_PROVIDERS', False):
|
||||
apps_to_hide.append('socialaccount')
|
||||
|
||||
app_list = [app for app in app_list if app['app_label'] not in apps_to_hide]
|
||||
|
||||
# 3. change the ordering
|
||||
app_order = {
|
||||
'files': 1,
|
||||
'users': 2,
|
||||
'socialaccount': 3,
|
||||
'rbac': 5,
|
||||
}
|
||||
|
||||
app_list.sort(key=lambda x: app_order.get(x['app_label'], 999))
|
||||
|
||||
return app_list
|
||||
|
||||
admin.AdminSite.get_app_list = get_app_list
|
||||
0
admin_customizations/migrations/__init__.py
Normal file
0
admin_customizations/migrations/__init__.py
Normal file
0
admin_customizations/models.py
Normal file
0
admin_customizations/models.py
Normal file
0
admin_customizations/tests.py
Normal file
0
admin_customizations/tests.py
Normal file
0
admin_customizations/views.py
Normal file
0
admin_customizations/views.py
Normal file
10
cli-tool/README.md
Normal file
10
cli-tool/README.md
Normal file
@@ -0,0 +1,10 @@
|
||||
## MediaCMS CLI Tool
|
||||
This is the CLI tool to interact with the API of your installation/instance of MediaCMS.
|
||||
|
||||
### How to configure and use the tools
|
||||
- Make sure that you have all the required installations (`cli-tool/requirements.txt`)installed. To install it -
|
||||
- Create a new virtualenv using any python virtualenv manager.
|
||||
- Then activate the virtualenv and enter `pip install -r requirements.txt`.
|
||||
- Create an .env file in this folder (`mediacms/cli-tool/`)
|
||||
- Run the cli tool using the command `python cli.py login`. This will authenticate you and store necessary creds for further authentications.
|
||||
- To check the credentials and necessary setup, run `python cli.py whoami`. This will show your details.
|
||||
167
cli-tool/cli.py
Normal file
167
cli-tool/cli.py
Normal file
@@ -0,0 +1,167 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import click
|
||||
import requests
|
||||
from decouple import config
|
||||
from rich import print
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
|
||||
console = Console()
|
||||
|
||||
print("Welcome to the CLI Tool of [bold blue]MediaCMS![/bold blue]", ":thumbs_up:")
|
||||
|
||||
|
||||
BASE_URL = 'https://demo.mediacms.io/api/v1'
|
||||
AUTH_KEY = ''
|
||||
USERNAME = ''
|
||||
EMAIL = ''
|
||||
|
||||
|
||||
def set_envs():
|
||||
with open('.env', 'r') as file:
|
||||
if not file.read(1):
|
||||
print("Use the Login command to set your credential environment variables")
|
||||
else:
|
||||
global AUTH_KEY, USERNAME, EMAIL
|
||||
AUTH_KEY = config('AUTH_KEY')
|
||||
USERNAME = config('USERNAME')
|
||||
EMAIL = config('EMAIL')
|
||||
|
||||
|
||||
set_envs()
|
||||
|
||||
|
||||
@click.group()
|
||||
def apis():
|
||||
"""A CLI wrapper for the MediaCMS API endpoints."""
|
||||
|
||||
|
||||
@apis.command()
|
||||
def login():
|
||||
"""Login to your account."""
|
||||
|
||||
email = input('Enter your email address: ')
|
||||
password = input('Enter your password: ')
|
||||
|
||||
data = {
|
||||
"email": f"{email}",
|
||||
"password": f"{password}",
|
||||
}
|
||||
|
||||
response = requests.post(url=f'{BASE_URL}/login', data=data)
|
||||
if response.status_code == 200:
|
||||
username = json.loads(response.text)["username"]
|
||||
with open(".env", "w") as file:
|
||||
file.writelines(f'AUTH_KEY={json.loads(response.text)["token"]}\n')
|
||||
file.writelines(f'EMAIL={json.loads(response.text)["email"]}\n')
|
||||
file.writelines(f'USERNAME={json.loads(response.text)["username"]}\n')
|
||||
print(f"Welcome to MediaCMS [bold blue]{username}[/bold blue]. Your auth creds have been suceesfully stored in the .env file", ":v:")
|
||||
else:
|
||||
print(f'Error: {"non_field_errors": ["User not found."]}')
|
||||
|
||||
|
||||
@apis.command()
|
||||
def upload_media():
|
||||
"""Upload media to the server"""
|
||||
|
||||
headers = {'authorization': f'Token {AUTH_KEY}'}
|
||||
|
||||
path = input('Enter the location of the file or directory where multiple files are present: ')
|
||||
|
||||
if os.path.isdir(path):
|
||||
for filename in os.listdir(path):
|
||||
files = {}
|
||||
abs = os.path.abspath(f"{path}/{filename}")
|
||||
files['media_file'] = open(f'{abs}', 'rb')
|
||||
response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files)
|
||||
if response.status_code == 201:
|
||||
print(f"[bold blue]{filename}[/bold blue] successfully uploaded!")
|
||||
else:
|
||||
print(f'Error: {response.text}')
|
||||
|
||||
else:
|
||||
files = {}
|
||||
files['media_file'] = open(f'{os.path.abspath(path)}', 'rb')
|
||||
response = requests.post(url=f'{BASE_URL}/media', headers=headers, files=files)
|
||||
if response.status_code == 201:
|
||||
print(f"[bold blue]{filename}[/bold blue] successfully uploaded!")
|
||||
else:
|
||||
print(f'Error: {response.text}')
|
||||
|
||||
|
||||
@apis.command()
|
||||
def my_media():
|
||||
"""List all my media"""
|
||||
|
||||
headers = {'authorization': f'Token {AUTH_KEY}'}
|
||||
response = requests.get(url=f'{BASE_URL}/media?author={USERNAME}', headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
data_json = json.loads(response.text)
|
||||
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("Name of the media")
|
||||
table.add_column("Media Type")
|
||||
table.add_column("State")
|
||||
|
||||
for data in data_json['results']:
|
||||
table.add_row(data['title'], data['media_type'], data['state'])
|
||||
console.print(table)
|
||||
|
||||
else:
|
||||
print(f'Could not get the media: {response.text}')
|
||||
|
||||
|
||||
@apis.command()
|
||||
def whoami():
|
||||
"""Shows the details of the authorized user"""
|
||||
headers = {'authorization': f'Token {AUTH_KEY}'}
|
||||
response = requests.get(url=f'{BASE_URL}/whoami', headers=headers)
|
||||
for data, value in json.loads(response.text).items():
|
||||
print(data, ' : ', value)
|
||||
|
||||
|
||||
@apis.command()
|
||||
def categories():
|
||||
"""List all categories."""
|
||||
response = requests.get(url=f'{BASE_URL}/categories')
|
||||
if response.status_code == 200:
|
||||
data_json = json.loads(response.text)
|
||||
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("Category")
|
||||
table.add_column("Description")
|
||||
|
||||
for data in data_json:
|
||||
table.add_row(data['title'], data['description'])
|
||||
|
||||
console.print(table)
|
||||
else:
|
||||
print(f'Could not get the categories: {response.text}')
|
||||
|
||||
|
||||
@apis.command()
|
||||
def encodings():
|
||||
"""List all encoding profiles"""
|
||||
response = requests.get(url=f'{BASE_URL}/encode_profiles/')
|
||||
if response.status_code == 200:
|
||||
data_json = json.loads(response.text)
|
||||
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("Name")
|
||||
table.add_column("Extension")
|
||||
table.add_column("Resolution")
|
||||
table.add_column("Codec")
|
||||
table.add_column("Description")
|
||||
|
||||
for data in data_json:
|
||||
table.add_row(data['name'], data['extension'], str(data['resolution']), data['codec'], data['description'])
|
||||
console.print(table)
|
||||
else:
|
||||
print(f'Could not get the encodings: {response.text}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
apis()
|
||||
4
cli-tool/requirements.txt
Normal file
4
cli-tool/requirements.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
click
|
||||
python-decouple
|
||||
requests
|
||||
rich
|
||||
@@ -1,4 +1,5 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .celery import app as celery_app
|
||||
|
||||
__all__ = ["celery_app"]
|
||||
|
||||
10
cms/auth_backends.py
Normal file
10
cms/auth_backends.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.backends import ModelBackend
|
||||
|
||||
|
||||
class ApprovalBackend(ModelBackend):
|
||||
def user_can_authenticate(self, user):
|
||||
can_authenticate = super().user_can_authenticate(user)
|
||||
if can_authenticate and settings.USERS_NEEDS_TO_BE_APPROVED and not user.is_superuser:
|
||||
return getattr(user, 'is_approved', False)
|
||||
return can_authenticate
|
||||
@@ -1,6 +1,9 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
|
||||
from celery import Celery
|
||||
from django.conf import settings
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cms.settings")
|
||||
app = Celery("cms")
|
||||
@@ -12,5 +15,8 @@ app.conf.beat_schedule = app.conf.CELERY_BEAT_SCHEDULE
|
||||
app.conf.broker_transport_options = {"visibility_timeout": 60 * 60 * 24} # 1 day
|
||||
# http://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#redis-caveats
|
||||
|
||||
# setting this to settings.py file only is not respected. Setting here too
|
||||
app.conf.task_always_eager = settings.CELERY_TASK_ALWAYS_EAGER
|
||||
|
||||
|
||||
app.conf.worker_prefetch_multiplier = 1
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.response import Response
|
||||
from collections import OrderedDict # requires Python 2.7 or later
|
||||
|
||||
from django.core.paginator import Paginator
|
||||
from django.utils.functional import cached_property
|
||||
from rest_framework.pagination import PageNumberPagination
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
class FasterDjangoPaginator(Paginator):
|
||||
@@ -17,7 +18,6 @@ class FastPaginationWithoutCount(PageNumberPagination):
|
||||
django_paginator_class = FasterDjangoPaginator
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
|
||||
return Response(
|
||||
OrderedDict(
|
||||
[
|
||||
|
||||
57
cms/dev_settings.py
Normal file
57
cms/dev_settings.py
Normal file
@@ -0,0 +1,57 @@
|
||||
# Development settings, used in docker-compose-dev.yaml
|
||||
import os
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"admin_customizations",
|
||||
"django.contrib.auth",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
"allauth.socialaccount",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"jazzmin",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.sites",
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
"imagekit",
|
||||
"files.apps.FilesConfig",
|
||||
"users.apps.UsersConfig",
|
||||
"actions.apps.ActionsConfig",
|
||||
"rbac.apps.RbacConfig",
|
||||
"identity_providers.apps.IdentityProvidersConfig",
|
||||
"debug_toolbar",
|
||||
"mptt",
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
"uploader.apps.UploaderConfig",
|
||||
"djcelery_email",
|
||||
"drf_yasg",
|
||||
"allauth.socialaccount.providers.saml",
|
||||
"saml_auth.apps.SamlAuthConfig",
|
||||
"corsheaders",
|
||||
"tinymce",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'debug_toolbar.middleware.DebugToolbarMiddleware',
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
]
|
||||
|
||||
DEBUG = True
|
||||
CORS_ORIGIN_ALLOW_ALL = True
|
||||
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static_collected')
|
||||
23
cms/middleware.py
Normal file
23
cms/middleware.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from django.conf import settings
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class ApprovalMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
if settings.USERS_NEEDS_TO_BE_APPROVED and request.user.is_authenticated and not request.user.is_superuser and not getattr(request.user, 'is_approved', False):
|
||||
allowed_paths = [
|
||||
reverse('approval_required'),
|
||||
reverse('account_logout'),
|
||||
]
|
||||
if request.path not in allowed_paths:
|
||||
if request.path.startswith('/api/'):
|
||||
return JsonResponse({'detail': 'User account not approved.'}, status=403)
|
||||
return redirect('approval_required')
|
||||
|
||||
response = self.get_response(request)
|
||||
return response
|
||||
@@ -1,13 +1,29 @@
|
||||
from django.conf import settings
|
||||
from rest_framework import permissions
|
||||
from files.methods import is_mediacms_editor, is_mediacms_manager
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from files.methods import (
|
||||
is_mediacms_editor,
|
||||
is_mediacms_manager,
|
||||
user_allowed_to_upload,
|
||||
)
|
||||
|
||||
|
||||
class IsAuthorizedToAdd(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
return user_allowed_to_upload(request)
|
||||
if not user_allowed_to_upload(request):
|
||||
raise PermissionDenied("You don't have permission to upload media, or have reached max number of media uploads.")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
class IsAuthorizedToAddComment(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
if request.method in permissions.SAFE_METHODS:
|
||||
return True
|
||||
return user_allowed_to_comment(request)
|
||||
|
||||
|
||||
class IsUserOrManager(permissions.BasePermission):
|
||||
@@ -24,7 +40,10 @@ class IsUserOrManager(permissions.BasePermission):
|
||||
if is_mediacms_manager(request.user):
|
||||
return True
|
||||
|
||||
return obj.user == request.user
|
||||
if hasattr(obj, 'user'):
|
||||
return obj.user == request.user
|
||||
else:
|
||||
return obj == request.user
|
||||
|
||||
|
||||
class IsUserOrEditor(permissions.BasePermission):
|
||||
@@ -44,21 +63,22 @@ class IsUserOrEditor(permissions.BasePermission):
|
||||
return obj.user == request.user
|
||||
|
||||
|
||||
def user_allowed_to_upload(request):
|
||||
def user_allowed_to_comment(request):
|
||||
"""Any custom logic for whether a user is allowed
|
||||
to upload content lives here
|
||||
to comment lives here
|
||||
"""
|
||||
if request.user.is_anonymous:
|
||||
return False
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
|
||||
if settings.CAN_ADD_MEDIA == "all":
|
||||
# Default is "all"
|
||||
if not hasattr(settings, "CAN_COMMENT") or settings.CAN_COMMENT == "all":
|
||||
return True
|
||||
elif settings.CAN_ADD_MEDIA == "email_verified":
|
||||
elif settings.CAN_COMMENT == "email_verified":
|
||||
if request.user.email_is_verified:
|
||||
return True
|
||||
elif settings.CAN_ADD_MEDIA == "advancedUser":
|
||||
elif settings.CAN_COMMENT == "advancedUser":
|
||||
if request.user.advancedUser:
|
||||
return True
|
||||
return False
|
||||
|
||||
371
cms/settings.py
371
cms/settings.py
@@ -1,29 +1,36 @@
|
||||
import os
|
||||
|
||||
from celery.schedules import crontab
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
DEBUG = False
|
||||
|
||||
# PORTAL NAME, this is the portal title and
|
||||
# is also shown on several places as emails
|
||||
PORTAL_NAME = "MediaCMS"
|
||||
LANGUAGE_CODE = "en-us"
|
||||
PORTAL_DESCRIPTION = ""
|
||||
TIME_ZONE = "Europe/London"
|
||||
|
||||
# who can add media
|
||||
# valid options include 'all', 'email_verified', 'advancedUser'
|
||||
CAN_ADD_MEDIA = "all"
|
||||
|
||||
# who can comment
|
||||
# valid options include 'all', 'email_verified', 'advancedUser'
|
||||
CAN_COMMENT = "all"
|
||||
|
||||
# valid choices here are 'public', 'private', 'unlisted
|
||||
PORTAL_WORKFLOW = "public"
|
||||
|
||||
DEFAULT_THEME = "black" # this is not taken under consideration currently
|
||||
# valid values: 'light', 'dark'.
|
||||
DEFAULT_THEME = "light"
|
||||
|
||||
|
||||
# These are passed on every request
|
||||
# if set to False will not fetch external content
|
||||
# this is only for the static files, as fonts/css/js files loaded from CDNs
|
||||
# not for user uploaded media!
|
||||
LOAD_FROM_CDN = True
|
||||
LOAD_FROM_CDN = False
|
||||
LOGIN_ALLOWED = True # whether the login button appears
|
||||
REGISTER_ALLOWED = True # whether the register button appears
|
||||
UPLOAD_MEDIA_ALLOWED = True # whether the upload media button appears
|
||||
@@ -42,7 +49,11 @@ ALLOW_RATINGS_CONFIRMED_EMAIL_ONLY = True
|
||||
|
||||
# ip of the server should be part of this
|
||||
ALLOWED_HOSTS = ["*", "mediacms.io", "127.0.0.1", "localhost"]
|
||||
|
||||
FRONTEND_HOST = "http://localhost"
|
||||
# this variable - along with SSL_FRONTEND_HOST is used on several places
|
||||
# as email where a URL need appear etc
|
||||
|
||||
# FRONTEND_HOST needs an http prefix - at the end of the file
|
||||
# there's a conversion to https with the SSL_FRONTEND_HOST env
|
||||
INTERNAL_IPS = "127.0.0.1"
|
||||
@@ -80,26 +91,55 @@ MAX_MEDIA_PER_PLAYLIST = 70
|
||||
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
|
||||
|
||||
MAX_CHARS_FOR_COMMENT = 10000 # so that it doesn't end up huge
|
||||
TIMESTAMP_IN_TIMEBAR = False # shows timestamped comments in the timebar for videos
|
||||
ALLOW_MENTION_IN_COMMENTS = False # allowing to mention other users with @ in the comments
|
||||
|
||||
# valid options: content, author
|
||||
RELATED_MEDIA_STRATEGY = "content"
|
||||
|
||||
# Whether or not to generate a sitemap.xml listing the pages on the site (default: False)
|
||||
GENERATE_SITEMAP = False
|
||||
|
||||
# Whether to include media count numbers on categories and tags listing pages
|
||||
INCLUDE_LISTING_NUMBERS = True
|
||||
|
||||
USE_I18N = True
|
||||
USE_L10N = True
|
||||
USE_TZ = True
|
||||
SITE_ID = 1
|
||||
|
||||
# these are the portal logos (dark and light)
|
||||
# set new paths for svg or png if you want to override
|
||||
# svg has priority over png, so if you want to use
|
||||
# custom pngs and not svgs, remove the lines with svgs
|
||||
# Logo paths (served from /static/)
|
||||
# Default logos are built into the image
|
||||
# To customize: place files in custom/static/images/ and reference as /custom/static/images/file.png
|
||||
# or set as empty strings to disable
|
||||
# example:
|
||||
# PORTAL_LOGO_DARK_PNG = "/custom/static/images/my-logo.png"
|
||||
# PORTAL_LOGO_DARK_SVG = ""
|
||||
PORTAL_LOGO_DARK_SVG = "/static/images/logo_dark.svg"
|
||||
PORTAL_LOGO_DARK_PNG = "/static/images/logo_dark.png"
|
||||
PORTAL_LOGO_LIGHT_SVG = "/static/images/logo_light.svg"
|
||||
PORTAL_LOGO_LIGHT_PNG = "/static/images/logo_dark.png"
|
||||
|
||||
# Extra CSS files to include in templates
|
||||
# To add custom CSS: place files in custom/static/css/ and add paths here
|
||||
# Use /custom/static/ prefix for files in custom/ directory
|
||||
# Example: EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
EXTRA_CSS_PATHS = []
|
||||
# protection agains anonymous users
|
||||
# per ip address limit, for actions as like/dislike/report
|
||||
TIME_TO_ACTION_ANONYMOUS = 10 * 60
|
||||
|
||||
# django-allauth settings
|
||||
ACCOUNT_SESSION_REMEMBER = True
|
||||
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
|
||||
ACCOUNT_LOGIN_METHODS = {"username", "email"}
|
||||
ACCOUNT_EMAIL_REQUIRED = True # new users need to specify email
|
||||
ACCOUNT_EMAIL_VERIFICATION = "optional" # 'mandatory' 'none'
|
||||
ACCOUNT_LOGIN_ON_EMAIL_CONFIRMATION = True
|
||||
ACCOUNT_USERNAME_MIN_LENGTH = "4"
|
||||
ACCOUNT_USERNAME_MIN_LENGTH = 4
|
||||
ACCOUNT_ADAPTER = "users.adapter.MyAccountAdapter"
|
||||
ACCOUNT_SIGNUP_FORM_CLASS = "users.forms.SignupForm"
|
||||
ACCOUNT_USERNAME_VALIDATORS = "users.validators.custom_username_validators"
|
||||
@@ -107,13 +147,19 @@ ACCOUNT_SIGNUP_PASSWORD_ENTER_TWICE = False
|
||||
ACCOUNT_USERNAME_REQUIRED = True
|
||||
ACCOUNT_LOGIN_ON_PASSWORD_RESET = True
|
||||
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
|
||||
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 20
|
||||
ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 5
|
||||
# registration won't be open, might also consider to remove links for register
|
||||
USERS_CAN_SELF_REGISTER = True
|
||||
|
||||
RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = ["xxx.com", "emaildomainwhatever.com"]
|
||||
|
||||
# by default users do not need to be approved. If this is set to True, then new users
|
||||
# will have to be approved before they can login successfully
|
||||
USERS_NEEDS_TO_BE_APPROVED = False
|
||||
|
||||
# Comma separated list of domains: ["organization.com", "private.organization.com", "org2.com"]
|
||||
# Empty list disables.
|
||||
ALLOWED_DOMAINS_FOR_USER_REGISTRATION = []
|
||||
|
||||
# django rest settings
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_AUTHENTICATION_CLASSES": (
|
||||
@@ -137,13 +183,20 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
STATIC_URL = "/static/" # where js/css files are stored on the filesystem
|
||||
MEDIA_URL = "/media/" # URL where static files are served from the server
|
||||
STATIC_ROOT = BASE_DIR + "/static/"
|
||||
# Additional locations for static files
|
||||
# Note: custom/static is NOT included here because it's served directly by nginx
|
||||
# at /custom/static/ and doesn't need collectstatic
|
||||
STATICFILES_DIRS = []
|
||||
# where uploaded + encoded media are stored
|
||||
MEDIA_ROOT = BASE_DIR + "/media_files/"
|
||||
|
||||
MEDIA_UPLOAD_DIR = os.path.join(MEDIA_ROOT, "original/")
|
||||
MEDIA_ENCODING_DIR = os.path.join(MEDIA_ROOT, "encoded/")
|
||||
THUMBNAIL_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "thumbnails/")
|
||||
SUBTITLES_UPLOAD_DIR = os.path.join(MEDIA_UPLOAD_DIR, "subtitles/")
|
||||
# these used to be os.path.join(MEDIA_ROOT, "folder/") but update to
|
||||
# Django 3.1.9 requires not absolute paths to be utilized...
|
||||
|
||||
MEDIA_UPLOAD_DIR = "original/"
|
||||
MEDIA_ENCODING_DIR = "encoded/"
|
||||
THUMBNAIL_UPLOAD_DIR = f"{MEDIA_UPLOAD_DIR}/thumbnails/"
|
||||
SUBTITLES_UPLOAD_DIR = f"{MEDIA_UPLOAD_DIR}/subtitles/"
|
||||
HLS_DIR = os.path.join(MEDIA_ROOT, "hls/")
|
||||
|
||||
FFMPEG_COMMAND = "ffmpeg" # this is the path
|
||||
@@ -165,7 +218,7 @@ CHUNKIZE_VIDEO_DURATION = 60 * 5
|
||||
VIDEO_CHUNKS_DURATION = 60 * 4
|
||||
|
||||
# always get these two, even if upscaling
|
||||
MINIMUM_RESOLUTIONS_TO_ENCODE = [240, 360]
|
||||
MINIMUM_RESOLUTIONS_TO_ENCODE = [144, 240]
|
||||
|
||||
# default settings for notifications
|
||||
# not all of them are implemented
|
||||
@@ -205,15 +258,13 @@ POST_UPLOAD_AUTHOR_MESSAGE_UNLISTED_NO_COMMENTARY = ""
|
||||
# only in case where unlisted workflow is used and no commentary
|
||||
# exists
|
||||
|
||||
CANNOT_ADD_MEDIA_MESSAGE = ""
|
||||
CANNOT_ADD_MEDIA_MESSAGE = "User cannot add media, or maximum number of media uploads has been reached."
|
||||
|
||||
# mp4hls command, part of Bendo4
|
||||
MP4HLS_COMMAND = (
|
||||
"/home/mediacms.io/mediacms/Bento4-SDK-1-6-0-632.x86_64-unknown-linux/bin/mp4hls"
|
||||
)
|
||||
# mp4hls command, part of Bento4
|
||||
MP4HLS_COMMAND = "/home/mediacms.io/bento4/bin/mp4hls"
|
||||
|
||||
# highly experimental, related with remote workers
|
||||
ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24"
|
||||
ADMIN_TOKEN = ""
|
||||
# this is used by remote workers to push
|
||||
# encodings once they are done
|
||||
# USE_BASIC_HTTP = True
|
||||
@@ -228,35 +279,6 @@ ADMIN_TOKEN = "c2b8e1838b6128asd333ddc5e24"
|
||||
# uncomment the two lines related to htpasswd
|
||||
|
||||
|
||||
CKEDITOR_CONFIGS = {
|
||||
"default": {
|
||||
"toolbar": "Custom",
|
||||
"width": "100%",
|
||||
"toolbar_Custom": [
|
||||
["Styles"],
|
||||
["Format"],
|
||||
["Bold", "Italic", "Underline"],
|
||||
["HorizontalRule"],
|
||||
[
|
||||
"NumberedList",
|
||||
"BulletedList",
|
||||
"-",
|
||||
"Outdent",
|
||||
"Indent",
|
||||
"-",
|
||||
"JustifyLeft",
|
||||
"JustifyCenter",
|
||||
"JustifyRight",
|
||||
"JustifyBlock",
|
||||
],
|
||||
["Link", "Unlink"],
|
||||
["Image"],
|
||||
["RemoveFormat", "Source"],
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AUTH_USER_MODEL = "users.User"
|
||||
LOGIN_REDIRECT_URL = "/"
|
||||
|
||||
@@ -266,7 +288,7 @@ AUTHENTICATION_BACKENDS = (
|
||||
)
|
||||
|
||||
INSTALLED_APPS = [
|
||||
"django.contrib.admin",
|
||||
"admin_customizations",
|
||||
"django.contrib.auth",
|
||||
"allauth",
|
||||
"allauth.account",
|
||||
@@ -275,6 +297,8 @@ INSTALLED_APPS = [
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"jazzmin",
|
||||
"django.contrib.admin",
|
||||
"django.contrib.sites",
|
||||
"rest_framework",
|
||||
"rest_framework.authtoken",
|
||||
@@ -282,23 +306,31 @@ INSTALLED_APPS = [
|
||||
"files.apps.FilesConfig",
|
||||
"users.apps.UsersConfig",
|
||||
"actions.apps.ActionsConfig",
|
||||
"rbac.apps.RbacConfig",
|
||||
"identity_providers.apps.IdentityProvidersConfig",
|
||||
"debug_toolbar",
|
||||
"mptt",
|
||||
"crispy_forms",
|
||||
"crispy_bootstrap5",
|
||||
"uploader.apps.UploaderConfig",
|
||||
"djcelery_email",
|
||||
"ckeditor",
|
||||
"drf_yasg",
|
||||
"allauth.socialaccount.providers.saml",
|
||||
"saml_auth.apps.SamlAuthConfig",
|
||||
"tinymce",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.locale.LocaleMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "cms.urls"
|
||||
@@ -326,11 +358,15 @@ WSGI_APPLICATION = "cms.wsgi.application"
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
"OPTIONS": {
|
||||
"user_attributes": ("username", "email", "first_name", "last_name"),
|
||||
"max_similarity": 0.7,
|
||||
},
|
||||
},
|
||||
{
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
"OPTIONS": {
|
||||
"min_length": 5,
|
||||
"min_length": 7,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -342,40 +378,30 @@ FILE_UPLOAD_HANDLERS = [
|
||||
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
|
||||
]
|
||||
|
||||
LOGS_DIR = os.path.join(BASE_DIR, "logs")
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"verbose": {
|
||||
"format": "%(levelname)s %(asctime)s %(module)s "
|
||||
"%(process)d %(thread)d %(message)s"
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.FileHandler",
|
||||
"filename": os.path.join(LOGS_DIR, "debug.log"),
|
||||
},
|
||||
},
|
||||
"loggers": {
|
||||
"django": {
|
||||
"handlers": ["file"],
|
||||
"level": "ERROR",
|
||||
"propagate": True,
|
||||
},
|
||||
"console": {
|
||||
"level": "DEBUG",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "verbose",
|
||||
}
|
||||
},
|
||||
"root": {"level": "INFO", "handlers": ["console"]},
|
||||
}
|
||||
|
||||
DATABASES = {
|
||||
"default": {
|
||||
"ENGINE": "django.db.backends.postgresql",
|
||||
"NAME": "mediacms",
|
||||
"HOST": "127.0.0.1",
|
||||
"PORT": "5432",
|
||||
"USER": "mediacms",
|
||||
"PASSWORD": "mediacms",
|
||||
}
|
||||
}
|
||||
DATABASES = {"default": {"ENGINE": "django.db.backends.postgresql", "NAME": "mediacms", "HOST": "db", "PORT": "5432", "USER": "mediacms", "PASSWORD": "mediacms", "OPTIONS": {'pool': True}}}
|
||||
|
||||
|
||||
REDIS_LOCATION = "redis://127.0.0.1:6379/1"
|
||||
REDIS_LOCATION = "redis://redis:6379/1"
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
@@ -419,19 +445,206 @@ CELERY_BEAT_SCHEDULE = {
|
||||
# TODO: beat, delete chunks from media root
|
||||
# chunks_dir after xx days...(also uploads_dir)
|
||||
|
||||
|
||||
LOCAL_INSTALL = False
|
||||
|
||||
# this is an option to make the whole portal available to logged in users only
|
||||
# it is placed here so it can be overrided on local_settings.py
|
||||
GLOBAL_LOGIN_REQUIRED = False
|
||||
|
||||
# TODO: separate settings on production/development more properly, for now
|
||||
# this should be ok
|
||||
CELERY_TASK_ALWAYS_EAGER = False
|
||||
if os.environ.get("TESTING"):
|
||||
CELERY_TASK_ALWAYS_EAGER = True
|
||||
|
||||
# if True, only show original, don't perform any action on videos
|
||||
DO_NOT_TRANSCODE_VIDEO = False
|
||||
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
LANGUAGES = [
|
||||
('ar', _('Arabic')),
|
||||
('bn', _('Bengali')),
|
||||
('da', _('Danish')),
|
||||
('nl', _('Dutch')),
|
||||
('en', _('English')),
|
||||
('fr', _('French')),
|
||||
('de', _('German')),
|
||||
('hi', _('Hindi')),
|
||||
('id', _('Indonesian')),
|
||||
('it', _('Italian')),
|
||||
('ja', _('Japanese')),
|
||||
('ko', _('Korean')),
|
||||
('pt', _('Portuguese')),
|
||||
('ru', _('Russian')),
|
||||
('zh-hans', _('Simplified Chinese')),
|
||||
('sl', _('Slovenian')),
|
||||
('zh-hant', _('Traditional Chinese')),
|
||||
('es', _('Spanish')),
|
||||
('tr', _('Turkish')),
|
||||
('el', _('Greek')),
|
||||
('ur', _('Urdu')),
|
||||
('he', _('Hebrew')),
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = 'en' # default language
|
||||
|
||||
TINYMCE_DEFAULT_CONFIG = {
|
||||
"theme": "silver",
|
||||
"height": 500,
|
||||
"resize": "both",
|
||||
"menubar": "file edit view insert format tools table help",
|
||||
"menu": {
|
||||
"format": {
|
||||
"title": "Format",
|
||||
"items": "blocks | bold italic underline strikethrough superscript subscript code | " "fontfamily fontsize align lineheight | " "forecolor backcolor removeformat",
|
||||
},
|
||||
},
|
||||
"plugins": "advlist,autolink,autosave,lists,link,image,charmap,print,preview,anchor,"
|
||||
"searchreplace,visualblocks,code,fullscreen,insertdatetime,media,table,paste,directionality,"
|
||||
"code,help,wordcount,emoticons,file,image,media",
|
||||
"toolbar": "undo redo | code preview | blocks | "
|
||||
"bold italic | alignleft aligncenter "
|
||||
"alignright alignjustify ltr rtl | bullist numlist outdent indent | "
|
||||
"removeformat | restoredraft help | image media",
|
||||
"branding": False, # remove branding
|
||||
"promotion": False, # remove promotion
|
||||
"body_class": "page-main-inner custom-page-wrapper", # class of the body element in tinymce
|
||||
"block_formats": "Paragraph=p; Heading 1=h1; Heading 2=h2; Heading 3=h3;",
|
||||
"formats": { # customize h2 to always have emphasis-large class
|
||||
"h2": {"block": "h2", "classes": "emphasis-large"},
|
||||
},
|
||||
"font_size_formats": "16px 18px 24px 32px",
|
||||
"images_upload_url": "/tinymce/upload/",
|
||||
"images_upload_handler": "tinymce.views.upload_image",
|
||||
"automatic_uploads": True,
|
||||
"file_picker_types": "image",
|
||||
"paste_data_images": True,
|
||||
"paste_as_text": False,
|
||||
"paste_enable_default_filters": True,
|
||||
"paste_word_valid_elements": "b,strong,i,em,h1,h2,h3,h4,h5,h6,p,br,a,ul,ol,li",
|
||||
"paste_retain_style_properties": "all",
|
||||
"paste_remove_styles": False,
|
||||
"paste_merge_formats": True,
|
||||
"sandbox_iframes": False,
|
||||
}
|
||||
|
||||
SPRITE_NUM_SECS = 10
|
||||
# number of seconds for sprite image.
|
||||
# If you plan to change this, you must also follow the instructions on admins_docs.md
|
||||
# to change the equivalent value in ./frontend/src/static/js/components/media-viewer/VideoViewer/index.js and then re-build frontend
|
||||
|
||||
# how many images will be shown on the slideshow
|
||||
SLIDESHOW_ITEMS = 30
|
||||
# this calculation is redundant most probably, setting as an option
|
||||
CALCULATE_MD5SUM = False
|
||||
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
||||
|
||||
# allow option to override the default admin url
|
||||
# keep the trailing slash
|
||||
DJANGO_ADMIN_URL = "admin/"
|
||||
|
||||
# this are used around a number of places and will need to be well documented!!!
|
||||
|
||||
USE_SAML = False
|
||||
USE_RBAC = False
|
||||
USE_IDENTITY_PROVIDERS = False
|
||||
JAZZMIN_UI_TWEAKS = {"theme": "flatly"}
|
||||
|
||||
USE_ROUNDED_CORNERS = True
|
||||
|
||||
ALLOW_VIDEO_TRIMMER = True
|
||||
|
||||
ALLOW_CUSTOM_MEDIA_URLS = False
|
||||
|
||||
# Whether to allow anonymous users to list all users
|
||||
ALLOW_ANONYMOUS_USER_LISTING = True
|
||||
|
||||
# Who can see the members page
|
||||
# valid choices are all, editors, admins
|
||||
CAN_SEE_MEMBERS_PAGE = "all"
|
||||
|
||||
# User search field setting
|
||||
# valid choices are name_username, name_username_email
|
||||
# this searches for users in the share media modal under my media
|
||||
USER_SEARCH_FIELD = "name_username"
|
||||
|
||||
# Maximum number of media a user can upload
|
||||
NUMBER_OF_MEDIA_USER_CAN_UPLOAD = 100
|
||||
|
||||
# ffmpeg options
|
||||
FFMPEG_DEFAULT_PRESET = "medium" # see https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||
|
||||
# If 'all' is in the list, no check is performed
|
||||
ALLOWED_MEDIA_UPLOAD_TYPES = ["video", "audio", "image", "pdf"]
|
||||
|
||||
# transcription options
|
||||
# the mediacms-full docker image needs to be used in order to be able to use transcription
|
||||
# if you are using the mediacms-full image, change USE_WHISPER_TRANSCRIBE to True
|
||||
USE_WHISPER_TRANSCRIBE = False
|
||||
|
||||
# by default all users can request a video to be transcribed. If you want to
|
||||
# allow only editors, set this to False
|
||||
USER_CAN_TRANSCRIBE_VIDEO = True
|
||||
|
||||
# Whisper transcribe options - https://github.com/openai/whisper
|
||||
WHISPER_MODEL = "base"
|
||||
|
||||
# show a custom text in the sidebar footer, otherwise the default will be shown if this is empty
|
||||
SIDEBAR_FOOTER_TEXT = ""
|
||||
|
||||
try:
|
||||
# keep a local_settings.py file for local overrides
|
||||
from .local_settings import *
|
||||
# Load custom settings from custom/local_settings.py
|
||||
import sys
|
||||
sys.path.insert(0, BASE_DIR)
|
||||
from custom.local_settings import * # noqa
|
||||
|
||||
# ALLOWED_HOSTS needs a url/ip
|
||||
ALLOWED_HOSTS.append(FRONTEND_HOST.replace("http://", "").replace("https://", ""))
|
||||
except ImportError:
|
||||
# local_settings not in use
|
||||
# custom/local_settings.py not in use or empty
|
||||
pass
|
||||
|
||||
# Don't add new settings below that could be overridden in local_settings.py!!!
|
||||
|
||||
if "http" not in FRONTEND_HOST:
|
||||
# FRONTEND_HOST needs a http:// preffix
|
||||
FRONTEND_HOST = f"http://{FRONTEND_HOST}"
|
||||
FRONTEND_HOST = f"http://{FRONTEND_HOST}" # noqa
|
||||
|
||||
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
|
||||
if LOCAL_INSTALL:
|
||||
SSL_FRONTEND_HOST = FRONTEND_HOST.replace("http", "https")
|
||||
else:
|
||||
SSL_FRONTEND_HOST = FRONTEND_HOST
|
||||
|
||||
|
||||
# CSRF_COOKIE_SECURE = True
|
||||
# SESSION_COOKIE_SECURE = True
|
||||
|
||||
PYSUBS_COMMAND = "pysubs2"
|
||||
|
||||
# the following is related to local development using docker
|
||||
# and docker-compose-dev.yaml
|
||||
try:
|
||||
DEVELOPMENT_MODE = os.environ.get("DEVELOPMENT_MODE")
|
||||
if DEVELOPMENT_MODE:
|
||||
# keep a dev_settings.py file for local overrides
|
||||
from .dev_settings import * # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
if GLOBAL_LOGIN_REQUIRED:
|
||||
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
|
||||
MIDDLEWARE.insert(auth_index + 1, "django.contrib.auth.middleware.LoginRequiredMiddleware")
|
||||
|
||||
|
||||
if USERS_NEEDS_TO_BE_APPROVED:
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'cms.auth_backends.ApprovalBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend',
|
||||
)
|
||||
auth_index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
|
||||
MIDDLEWARE.insert(auth_index + 1, "cms.middleware.ApprovalMiddleware")
|
||||
|
||||
43
cms/urls.py
43
cms/urls.py
@@ -1,13 +1,38 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.conf.urls import url, include
|
||||
import debug_toolbar
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include
|
||||
from django.contrib import admin
|
||||
from django.urls import path, re_path
|
||||
from django.views.generic.base import TemplateView
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
from rest_framework.permissions import AllowAny
|
||||
|
||||
schema_view = get_schema_view(
|
||||
openapi.Info(title="MediaCMS API", default_version='v1', contact=openapi.Contact(url="https://mediacms.io"), x_logo={"url": "../../static/images/logo_dark.svg"}),
|
||||
public=True,
|
||||
permission_classes=(AllowAny,),
|
||||
)
|
||||
|
||||
# refactor seriously
|
||||
|
||||
urlpatterns = [
|
||||
url(r"^__debug__/", include(debug_toolbar.urls)),
|
||||
url(r"^", include("files.urls")),
|
||||
url(r"^", include("users.urls")),
|
||||
url(r"^accounts/", include("allauth.urls")),
|
||||
url(r"^api-auth/", include("rest_framework.urls")),
|
||||
path("admin/", admin.site.urls),
|
||||
re_path(r"^__debug__/", include(debug_toolbar.urls)),
|
||||
path(
|
||||
"robots.txt",
|
||||
TemplateView.as_view(template_name="robots.txt", content_type="text/plain"),
|
||||
),
|
||||
re_path(r"^", include("files.urls")),
|
||||
re_path(r"^", include("users.urls")),
|
||||
re_path(r"^accounts/", include("allauth.urls")),
|
||||
re_path(r"^api-auth/", include("rest_framework.urls")),
|
||||
path(settings.DJANGO_ADMIN_URL, admin.site.urls),
|
||||
re_path(r'^swagger(?P<format>\.json|\.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||
re_path(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
path('docs/api/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
path("tinymce/", include("tinymce.urls")),
|
||||
]
|
||||
|
||||
admin.site.site_header = "MediaCMS Admin"
|
||||
admin.site.site_title = "MediaCMS"
|
||||
admin.site.index_title = "Admin"
|
||||
|
||||
1
cms/version.py
Normal file
1
cms/version.py
Normal file
@@ -0,0 +1 @@
|
||||
VERSION = "7.2.1"
|
||||
99
config/imagemagick/policy.xml
Normal file
99
config/imagemagick/policy.xml
Normal file
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE policymap [
|
||||
<!ELEMENT policymap (policy)*>
|
||||
<!ATTLIST policymap xmlns CDATA #FIXED ''>
|
||||
<!ELEMENT policy EMPTY>
|
||||
<!ATTLIST policy xmlns CDATA #FIXED '' domain NMTOKEN #REQUIRED
|
||||
name NMTOKEN #IMPLIED pattern CDATA #IMPLIED rights NMTOKEN #IMPLIED
|
||||
stealth NMTOKEN #IMPLIED value CDATA #IMPLIED>
|
||||
]>
|
||||
<!--
|
||||
Configure ImageMagick policies.
|
||||
|
||||
Domains include system, delegate, coder, filter, path, or resource.
|
||||
|
||||
Rights include none, read, write, execute and all. Use | to combine them,
|
||||
for example: "read | write" to permit read from, or write to, a path.
|
||||
|
||||
Use a glob expression as a pattern.
|
||||
|
||||
Suppose we do not want users to process MPEG video images:
|
||||
|
||||
<policy domain="delegate" rights="none" pattern="mpeg:decode" />
|
||||
|
||||
Here we do not want users reading images from HTTP:
|
||||
|
||||
<policy domain="coder" rights="none" pattern="HTTP" />
|
||||
|
||||
The /repository file system is restricted to read only. We use a glob
|
||||
expression to match all paths that start with /repository:
|
||||
|
||||
<policy domain="path" rights="read" pattern="/repository/*" />
|
||||
|
||||
Lets prevent users from executing any image filters:
|
||||
|
||||
<policy domain="filter" rights="none" pattern="*" />
|
||||
|
||||
Any large image is cached to disk rather than memory:
|
||||
|
||||
<policy domain="resource" name="area" value="1GP"/>
|
||||
|
||||
Use the default system font unless overwridden by the application:
|
||||
|
||||
<policy domain="system" name="font" value="/usr/share/fonts/favorite.ttf"/>
|
||||
|
||||
Define arguments for the memory, map, area, width, height and disk resources
|
||||
with SI prefixes (.e.g 100MB). In addition, resource policies are maximums
|
||||
for each instance of ImageMagick (e.g. policy memory limit 1GB, -limit 2GB
|
||||
exceeds policy maximum so memory limit is 1GB).
|
||||
|
||||
Rules are processed in order. Here we want to restrict ImageMagick to only
|
||||
read or write a small subset of proven web-safe image types:
|
||||
|
||||
<policy domain="delegate" rights="none" pattern="*" />
|
||||
<policy domain="filter" rights="none" pattern="*" />
|
||||
<policy domain="coder" rights="none" pattern="*" />
|
||||
<policy domain="coder" rights="read|write" pattern="{GIF,JPEG,PNG,WEBP}" />
|
||||
-->
|
||||
<policymap>
|
||||
<!-- <policy domain="resource" name="temporary-path" value="/tmp"/> -->
|
||||
<policy domain="resource" name="memory" value="1GiB"/>
|
||||
<policy domain="resource" name="map" value="30GiB"/>
|
||||
<policy domain="resource" name="width" value="16MP"/>
|
||||
<policy domain="resource" name="height" value="16MP"/>
|
||||
<!-- <policy domain="resource" name="list-length" value="128"/> -->
|
||||
<policy domain="resource" name="area" value="40GP"/>
|
||||
<policy domain="resource" name="disk" value="100GiB"/>
|
||||
<!-- <policy domain="resource" name="file" value="768"/> -->
|
||||
<!-- <policy domain="resource" name="thread" value="4"/> -->
|
||||
<!-- <policy domain="resource" name="throttle" value="0"/> -->
|
||||
<!-- <policy domain="resource" name="time" value="3600"/> -->
|
||||
<!-- <policy domain="coder" rights="none" pattern="MVG" /> -->
|
||||
<!-- <policy domain="module" rights="none" pattern="{PS,PDF,XPS}" /> -->
|
||||
<!-- <policy domain="path" rights="none" pattern="@*" /> -->
|
||||
<!-- <policy domain="cache" name="memory-map" value="anonymous"/> -->
|
||||
<!-- <policy domain="cache" name="synchronize" value="True"/> -->
|
||||
<!-- <policy domain="cache" name="shared-secret" value="passphrase" stealth="true"/>
|
||||
<!-- <policy domain="system" name="max-memory-request" value="256MiB"/> -->
|
||||
<!-- <policy domain="system" name="shred" value="2"/> -->
|
||||
<!-- <policy domain="system" name="precision" value="6"/> -->
|
||||
<!-- <policy domain="system" name="font" value="/path/to/font.ttf"/> -->
|
||||
<!-- <policy domain="system" name="pixel-cache-memory" value="anonymous"/> -->
|
||||
<!-- <policy domain="system" name="shred" value="2"/> -->
|
||||
<!-- <policy domain="system" name="precision" value="6"/> -->
|
||||
<!-- not needed due to the need to use explicitly by mvg: -->
|
||||
<!-- <policy domain="delegate" rights="none" pattern="MVG" /> -->
|
||||
<!-- use curl -->
|
||||
<policy domain="delegate" rights="none" pattern="URL" />
|
||||
<policy domain="delegate" rights="none" pattern="HTTPS" />
|
||||
<policy domain="delegate" rights="none" pattern="HTTP" />
|
||||
<!-- in order to avoid to get image with password text -->
|
||||
<policy domain="path" rights="none" pattern="@*"/>
|
||||
<!-- disable ghostscript format types -->
|
||||
<policy domain="coder" rights="none" pattern="PS" />
|
||||
<policy domain="coder" rights="none" pattern="PS2" />
|
||||
<policy domain="coder" rights="none" pattern="PS3" />
|
||||
<policy domain="coder" rights="none" pattern="EPS" />
|
||||
<policy domain="coder" rights="none" pattern="PDF" />
|
||||
<policy domain="coder" rights="none" pattern="XPS" />
|
||||
</policymap>
|
||||
1
config/nginx-proxy/client_max_body_size.conf
Normal file
1
config/nginx-proxy/client_max_body_size.conf
Normal file
@@ -0,0 +1 @@
|
||||
client_max_body_size 5800M;
|
||||
41
config/nginx/nginx.conf
Normal file
41
config/nginx/nginx.conf
Normal file
@@ -0,0 +1,41 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
pid /run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 10240;
|
||||
}
|
||||
|
||||
worker_rlimit_nofile 20000; #each connection needs a filehandle (or 2 if you are proxying)
|
||||
http {
|
||||
proxy_connect_timeout 75;
|
||||
proxy_read_timeout 12000;
|
||||
client_max_body_size 5800M;
|
||||
sendfile on;
|
||||
tcp_nopush on;
|
||||
tcp_nodelay on;
|
||||
keepalive_timeout 10;
|
||||
types_hash_max_size 2048;
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
access_log /var/log/mediacms/nginx-main.access.log;
|
||||
error_log /var/log/mediacms/nginx-main.error.log;
|
||||
|
||||
gzip on;
|
||||
gzip_disable "msie6";
|
||||
|
||||
log_format compression '$remote_addr - $remote_user [$time_local] '
|
||||
'"$request" $status $body_bytes_sent '
|
||||
'"$http_referer" "$http_user_agent" "$gzip_ratio"';
|
||||
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
|
||||
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
include /etc/nginx/sites-enabled/*;
|
||||
}
|
||||
|
||||
38
config/nginx/site.conf
Normal file
38
config/nginx/site.conf
Normal file
@@ -0,0 +1,38 @@
|
||||
server {
|
||||
listen 80 ;
|
||||
|
||||
gzip on;
|
||||
access_log /var/log/mediacms/nginx.access.log;
|
||||
|
||||
error_log /var/log/mediacms/nginx.error.log warn;
|
||||
|
||||
location /static {
|
||||
alias /var/www/static ;
|
||||
}
|
||||
|
||||
location /custom/static {
|
||||
alias /var/www/custom ;
|
||||
}
|
||||
|
||||
location /media/original {
|
||||
alias /var/www/media/original;
|
||||
}
|
||||
|
||||
location /media {
|
||||
alias /var/www/media ;
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
}
|
||||
|
||||
location / {
|
||||
add_header 'Access-Control-Allow-Origin' '*';
|
||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
|
||||
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
|
||||
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
|
||||
|
||||
include /etc/nginx/uwsgi_params;
|
||||
uwsgi_pass web:9000;
|
||||
}
|
||||
}
|
||||
24
config/uwsgi/uwsgi.ini
Normal file
24
config/uwsgi/uwsgi.ini
Normal file
@@ -0,0 +1,24 @@
|
||||
[uwsgi]
|
||||
|
||||
chdir = /home/mediacms.io/mediacms/
|
||||
virtualenv = /home/mediacms.io
|
||||
module = cms.wsgi
|
||||
|
||||
uid=www-data
|
||||
gid=www-data
|
||||
|
||||
processes = 2
|
||||
threads = 2
|
||||
|
||||
master = true
|
||||
|
||||
socket = 0.0.0.0:9000
|
||||
|
||||
workers = 2
|
||||
|
||||
vacuum = true
|
||||
|
||||
hook-master-start = unix_signal:15 gracefully_kill_them_all
|
||||
need-app = true
|
||||
die-on-term = true
|
||||
buffer-size=32768
|
||||
0
custom/.gitkeep
Normal file
0
custom/.gitkeep
Normal file
238
custom/README.md
Normal file
238
custom/README.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Custom Configuration
|
||||
|
||||
This directory allows you to customize MediaCMS without modifying the codebase or rebuilding images.
|
||||
|
||||
## How It Works - Production Ready!
|
||||
|
||||
**The Flow:**
|
||||
|
||||
```
|
||||
1. CI/CD builds base image: docker build (no custom files)
|
||||
↓
|
||||
Pushes to Docker Hub
|
||||
|
||||
2. Production pulls image: docker compose pull
|
||||
↓
|
||||
Mounts custom/ directory
|
||||
|
||||
3. You add files: custom/static/css/custom.css
|
||||
custom/static/images/logo.png
|
||||
↓
|
||||
Nginx serves directly!
|
||||
|
||||
4. You reference in settings: EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
PORTAL_LOGO_DARK_PNG = "/custom/static/images/logo.png"
|
||||
↓
|
||||
Restart containers
|
||||
|
||||
5. Done! No rebuild needed!
|
||||
```
|
||||
|
||||
**Key Points:**
|
||||
- ✅ Files go in `custom/static/` on your host
|
||||
- ✅ Nginx serves them directly from `/custom/static/` URL
|
||||
- ✅ **NO rebuild needed** - just restart containers!
|
||||
- ✅ Works with pre-built images from Docker Hub
|
||||
- ✅ Perfect for production deployments
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: No Customization (Default)
|
||||
Just run docker compose - everything works out of the box:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### Option 2: With Customization
|
||||
Add your custom files, then restart:
|
||||
```bash
|
||||
# 1. Copy example settings
|
||||
cp custom/local_settings.py.example custom/local_settings.py
|
||||
|
||||
# 2. Edit settings
|
||||
nano custom/local_settings.py
|
||||
|
||||
# 3. Restart containers (no rebuild!)
|
||||
docker compose restart web celery_beat celery_short celery_long
|
||||
```
|
||||
|
||||
## Customization Options
|
||||
|
||||
### 1. Django Settings (`local_settings.py`)
|
||||
|
||||
**Create the file:**
|
||||
```bash
|
||||
cp custom/local_settings.py.example custom/local_settings.py
|
||||
```
|
||||
|
||||
**Edit with your settings:**
|
||||
```python
|
||||
# custom/local_settings.py
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = ['example.com']
|
||||
PORTAL_NAME = "My Media Site"
|
||||
```
|
||||
|
||||
**Apply changes (restart only - no rebuild):**
|
||||
```bash
|
||||
docker compose restart web celery_beat celery_short celery_long
|
||||
```
|
||||
|
||||
### 2. Custom Logo
|
||||
|
||||
**Add your logo:**
|
||||
```bash
|
||||
cp ~/my-logo.png custom/static/images/logo_dark.png
|
||||
```
|
||||
|
||||
**Reference it in settings:**
|
||||
```bash
|
||||
cat >> custom/local_settings.py <<EOF
|
||||
PORTAL_LOGO_DARK_PNG = "/custom/static/images/logo_dark.png"
|
||||
EOF
|
||||
```
|
||||
|
||||
**Restart (no rebuild needed!):**
|
||||
```bash
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
### 3. Custom CSS
|
||||
|
||||
**Create CSS file:**
|
||||
```bash
|
||||
cat > custom/static/css/custom.css <<EOF
|
||||
body {
|
||||
font-family: 'Arial', sans-serif;
|
||||
}
|
||||
.header {
|
||||
background-color: #333;
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
**Reference it in settings:**
|
||||
```bash
|
||||
cat >> custom/local_settings.py <<EOF
|
||||
EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
EOF
|
||||
```
|
||||
|
||||
**Restart (no rebuild needed!):**
|
||||
```bash
|
||||
docker compose restart web
|
||||
```
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
custom/
|
||||
├── README.md # This file
|
||||
├── local_settings.py.example # Template (copy to local_settings.py)
|
||||
├── local_settings.py # Your settings (gitignored)
|
||||
└── static/
|
||||
├── images/ # Custom logos (gitignored)
|
||||
│ └── logo_dark.png
|
||||
└── css/ # Custom CSS (gitignored)
|
||||
└── custom.css
|
||||
```
|
||||
|
||||
## Important Notes
|
||||
|
||||
✅ **No rebuild needed** - nginx serves custom/ files directly
|
||||
✅ **Works with pre-built images** - perfect for production
|
||||
✅ **Files are gitignored** - your customizations won't be committed
|
||||
✅ **Settings need restart only** - just restart containers
|
||||
✅ **Static files also just restart** - served directly by nginx
|
||||
|
||||
## Complete Example
|
||||
|
||||
```bash
|
||||
# 1. Create settings file
|
||||
cp custom/local_settings.py.example custom/local_settings.py
|
||||
|
||||
# 2. Add custom logo
|
||||
cp ~/logo.png custom/static/images/logo_dark.png
|
||||
|
||||
# 3. Add custom CSS
|
||||
echo "body { background: #f5f5f5; }" > custom/static/css/custom.css
|
||||
|
||||
# 4. Configure settings to use them
|
||||
cat >> custom/local_settings.py <<EOF
|
||||
|
||||
# Custom branding
|
||||
PORTAL_NAME = "My Media Portal"
|
||||
PORTAL_LOGO_DARK_PNG = "/custom/static/images/logo_dark.png"
|
||||
EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
|
||||
# Security
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = ['media.example.com']
|
||||
EOF
|
||||
|
||||
# 5. Apply changes (just restart!)
|
||||
docker compose restart web
|
||||
|
||||
# Done! No rebuild needed.
|
||||
```
|
||||
|
||||
## URL Paths Explained
|
||||
|
||||
| Your file | nginx serves at | You reference as |
|
||||
|-----------|----------------|------------------|
|
||||
| `custom/static/css/custom.css` | `http://localhost/custom/static/css/custom.css` | `"/custom/static/css/custom.css"` |
|
||||
| `custom/static/images/logo.png` | `http://localhost/custom/static/images/logo.png` | `"/custom/static/images/logo.png"` |
|
||||
|
||||
**Why `/custom/static/`?**
|
||||
- Distinguishes from core `/static/` (built into image)
|
||||
- Allows nginx to serve from different mount point
|
||||
- No rebuild needed when files change
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Changes not appearing?**
|
||||
- Restart containers: `docker compose restart web nginx`
|
||||
- Check nginx has custom/ mounted: `docker compose exec nginx ls /var/www/custom`
|
||||
- Check file exists: `docker compose exec nginx ls /var/www/custom/css/`
|
||||
- Test URL: `curl http://localhost/custom/static/css/custom.css`
|
||||
|
||||
**Import errors?**
|
||||
- Make sure `local_settings.py` has valid Python syntax
|
||||
- Check logs: `docker compose logs web`
|
||||
|
||||
**Logo not showing?**
|
||||
- Verify file is in `custom/static/images/`
|
||||
- Check path in `local_settings.py` uses `/custom/static/` prefix
|
||||
- Restart web container: `docker compose restart web`
|
||||
|
||||
## Advanced: Multiple CSS Files
|
||||
|
||||
```python
|
||||
# custom/local_settings.py
|
||||
EXTRA_CSS_PATHS = [
|
||||
"/custom/static/css/colors.css",
|
||||
"/custom/static/css/fonts.css",
|
||||
"/custom/static/css/layout.css",
|
||||
]
|
||||
```
|
||||
|
||||
## Advanced: Environment-Specific Settings
|
||||
|
||||
```python
|
||||
# custom/local_settings.py
|
||||
import os
|
||||
|
||||
if os.getenv('ENVIRONMENT') == 'production':
|
||||
DEBUG = False
|
||||
ALLOWED_HOSTS = ['media.example.com']
|
||||
else:
|
||||
DEBUG = True
|
||||
ALLOWED_HOSTS = ['*']
|
||||
```
|
||||
|
||||
Then set in docker-compose.yaml:
|
||||
```yaml
|
||||
web:
|
||||
environment:
|
||||
ENVIRONMENT: production
|
||||
```
|
||||
57
custom/local_settings.py.example
Normal file
57
custom/local_settings.py.example
Normal file
@@ -0,0 +1,57 @@
|
||||
# MediaCMS Local Settings Example
|
||||
# Copy this file to local_settings.py and customize as needed:
|
||||
# cp custom/local_settings.py.example custom/local_settings.py
|
||||
|
||||
# ===== Basic Settings =====
|
||||
|
||||
# DEBUG = False
|
||||
# ALLOWED_HOSTS = ['example.com', 'www.example.com']
|
||||
# PORTAL_NAME = "My Media Portal"
|
||||
|
||||
# ===== Database Settings =====
|
||||
|
||||
# DATABASES = {
|
||||
# 'default': {
|
||||
# 'ENGINE': 'django.db.backends.postgresql',
|
||||
# 'NAME': 'mediacms',
|
||||
# 'USER': 'mediacms',
|
||||
# 'PASSWORD': 'mediacms',
|
||||
# 'HOST': 'db',
|
||||
# 'PORT': '5432',
|
||||
# }
|
||||
# }
|
||||
|
||||
# ===== Custom Branding =====
|
||||
|
||||
# Custom logos (place files in custom/static/images/)
|
||||
# Nginx serves these directly from /custom/static/ (no rebuild needed!)
|
||||
# PORTAL_LOGO_DARK_SVG = "/custom/static/images/logo_dark.svg"
|
||||
# PORTAL_LOGO_DARK_PNG = "/custom/static/images/logo_dark.png"
|
||||
# PORTAL_LOGO_LIGHT_SVG = "/custom/static/images/logo_light.svg"
|
||||
# PORTAL_LOGO_LIGHT_PNG = "/custom/static/images/logo_light.png"
|
||||
|
||||
# Custom CSS (place files in custom/static/css/)
|
||||
# Nginx serves these directly from /custom/static/ (no rebuild needed!)
|
||||
# EXTRA_CSS_PATHS = ["/custom/static/css/custom.css"]
|
||||
|
||||
# ===== Email Settings =====
|
||||
|
||||
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
# EMAIL_HOST = 'smtp.gmail.com'
|
||||
# EMAIL_PORT = 587
|
||||
# EMAIL_USE_TLS = True
|
||||
# EMAIL_HOST_USER = 'your-email@example.com'
|
||||
# EMAIL_HOST_PASSWORD = 'your-password'
|
||||
# DEFAULT_FROM_EMAIL = 'noreply@example.com'
|
||||
|
||||
# ===== Security Settings =====
|
||||
|
||||
# SECRET_KEY = 'your-secret-key-here'
|
||||
# SECURE_SSL_REDIRECT = True
|
||||
# SESSION_COOKIE_SECURE = True
|
||||
# CSRF_COOKIE_SECURE = True
|
||||
|
||||
# ===== Other Settings =====
|
||||
|
||||
# Any other Django setting can be overridden here
|
||||
# See cms/settings.py for available settings
|
||||
0
custom/static/.gitkeep
Normal file
0
custom/static/.gitkeep
Normal file
0
custom/static/css/.gitkeep
Normal file
0
custom/static/css/.gitkeep
Normal file
0
custom/static/images/.gitkeep
Normal file
0
custom/static/images/.gitkeep
Normal file
75
deic_setup_notes.md
Normal file
75
deic_setup_notes.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# MediaCMS: Document Changes for DEIC
|
||||
|
||||
## Configuration Changes
|
||||
The following changes are required in `config/local_settings.py`:
|
||||
|
||||
```python
|
||||
|
||||
# default workflow
|
||||
PORTAL_WORKFLOW = 'private'
|
||||
|
||||
# Authentication Settings
|
||||
# these two are necessary so that users cannot register through system accounts. They can only register through identity providers
|
||||
REGISTER_ALLOWED = False
|
||||
USERS_CAN_SELF_REGISTER = False
|
||||
|
||||
USE_RBAC = True
|
||||
USE_SAML = True
|
||||
USE_IDENTITY_PROVIDERS = True
|
||||
|
||||
# Proxy and SSL Settings
|
||||
USE_X_FORWARDED_HOST = True
|
||||
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
SECURE_SSL_REDIRECT = True
|
||||
CSRF_COOKIE_SECURE = True
|
||||
SESSION_COOKIE_SECURE = True
|
||||
|
||||
# SAML Configuration
|
||||
SOCIALACCOUNT_ADAPTER = 'saml_auth.adapter.SAMLAccountAdapter'
|
||||
ACCOUNT_USERNAME_VALIDATORS = "users.validators.less_restrictive_username_validators"
|
||||
SOCIALACCOUNT_PROVIDERS = {
|
||||
"saml": {
|
||||
"provider_class": "saml_auth.custom.provider.CustomSAMLProvider",
|
||||
}
|
||||
}
|
||||
SOCIALACCOUNT_AUTO_SIGNUP = True
|
||||
SOCIALACCOUNT_EMAIL_REQUIRED = False
|
||||
|
||||
# if set to strict, user is created with the email from the saml provider without
|
||||
# checking if the email is already on the system
|
||||
# however if this is ommited, and user tries to login with an email that already exists on
|
||||
# the system, then they get to the ugly form where it suggests they add a username/email/name
|
||||
|
||||
ACCOUNT_PREVENT_ENUMERATION = 'strict'
|
||||
|
||||
```
|
||||
|
||||
## SAML Configuration Steps
|
||||
|
||||
### Step 1: Add SAML Identity Provider
|
||||
1. Navigate to Admin panel
|
||||
2. Select "Identity Provider"
|
||||
3. Configure as follows:
|
||||
- **Provider**: saml # ensure this is set with lower case!
|
||||
- **Provider ID**: `wayf.wayf.dk`
|
||||
- **IDP Config Name**: `Deic` (or preferred name)
|
||||
- **Client ID**: `wayf_dk` (important: defines the URL, e.g., `https://deic.mediacms.io/accounts/saml/wayf_dk`)
|
||||
- **Site**: Set the default one
|
||||
|
||||
### Step 2: Add SAML Configuration
|
||||
Can be set through the SAML Configurations tab:
|
||||
|
||||
1. **IDP ID**: Must be a URL, e.g., `https://wayf.wayf.dk`
|
||||
2. **IDP Certificate**: x509cert from your SAML provider
|
||||
3. **SSO URL**: `https://wayf.wayf.dk/saml2/idp/SSOService2.php`
|
||||
4. **SLO URL**: `https://wayf.wayf.dk/saml2/idp/SingleLogoutService.php`
|
||||
5. **SP Metadata URL**: The metadata URL set for the SP, e.g., `https://deic.mediacms.io/saml/metadata`. This should point to the URL of the SP and is autogenerated
|
||||
|
||||
### Step 3: Set the other Options
|
||||
1. **Email Settings**:
|
||||
- `verified_email`: When enabled, emails from SAML responses will be marked as verified
|
||||
- `Remove from groups`: When enabled, user is removed from a group after login, if they have been removed from the group on the IDP
|
||||
2. **Global Role Mapping**: Maps the role returned by SAML (as set in the SAML Configuration tab) with the role in MediaCMS
|
||||
3. **Group Role Mapping**: Maps the role returned by SAML (as set in the SAML Configuration tab) with the role in groups that user will be added
|
||||
4. **Group mapping**: This creates groups associated with this IDP. Group ids as they come from SAML, associated with MediaCMS groups
|
||||
5. **Category Mapping**: This maps a group id (from SAML response) with a category in MediaCMS
|
||||
@@ -1,31 +0,0 @@
|
||||
[Unit]
|
||||
Description=MediaCMS celery long queue
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
Environment=CELERYD_NODES="long1"
|
||||
Environment=CELERY_QUEUE="long_tasks"
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
Environment=CELERY_APP="cms"
|
||||
Environment=CELERYD_MULTI="multi"
|
||||
Environment=CELERYD_OPTS="-Ofair --prefetch-multiplier=1"
|
||||
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid"
|
||||
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log"
|
||||
Environment=CELERYD_LOG_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
|
||||
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
|
||||
ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -8,15 +8,13 @@ User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
Environment=CELERY_APP="cms"
|
||||
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/beat%n.pid"
|
||||
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/beat%N.log"
|
||||
Environment=CELERYD_LOG_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} beat -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR}'
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms beat --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL}'
|
||||
ExecStop=/bin/kill -s TERM $MAINPID
|
||||
|
||||
[Install]
|
||||
29
deploy/local_install/celery_long.service
Normal file
29
deploy/local_install/celery_long.service
Normal file
@@ -0,0 +1,29 @@
|
||||
[Unit]
|
||||
Description=MediaCMS celery long queue
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
Environment=CELERYD_NODES="long1"
|
||||
Environment=CELERY_QUEUE="long_tasks"
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
Environment=CELERYD_MULTI="multi"
|
||||
Environment=CELERYD_OPTS="-Ofair --prefetch-multiplier=1"
|
||||
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid"
|
||||
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log"
|
||||
Environment=CELERYD_LOG_LEVEL="INFO"
|
||||
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms multi start ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}'
|
||||
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
|
||||
ExecReload=/bin/sh -c '${CELERY_BIN} -A cms multi restart ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
||||
@@ -8,14 +8,13 @@ User=www-data
|
||||
Group=www-data
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
WorkingDirectory=/home/mediacms.io/mediacms
|
||||
Environment=CELERYD_NODES="short1 short2"
|
||||
Environment=CELERY_QUEUE="short_tasks"
|
||||
# Absolute or relative path to the 'celery' command:
|
||||
Environment=CELERY_BIN="/home/mediacms.io/bin/celery"
|
||||
# App instance to use
|
||||
# comment out this line if you don't use an app
|
||||
Environment=CELERY_APP="cms"
|
||||
# or fully qualified:
|
||||
#CELERY_APP="proj.tasks:app"
|
||||
# How to call manage.py
|
||||
@@ -28,13 +27,12 @@ Environment=CELERYD_OPTS="--soft-time-limit=300 -c10"
|
||||
Environment=CELERYD_PID_FILE="/home/mediacms.io/mediacms/pids/%n.pid"
|
||||
Environment=CELERYD_LOG_FILE="/home/mediacms.io/mediacms/logs/%N.log"
|
||||
Environment=CELERYD_LOG_LEVEL="INFO"
|
||||
Environment=APP_DIR="/home/mediacms.io/mediacms"
|
||||
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
|
||||
ExecStart=/bin/sh -c '${CELERY_BIN} -A cms multi start ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}'
|
||||
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
ExecStop=/bin/sh -c '${CELERY_BIN} -A cms multi stopwait ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE}'
|
||||
|
||||
ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} --workdir=${APP_DIR} -Q ${CELERY_QUEUE}'
|
||||
ExecReload=/bin/sh -c '${CELERY_BIN} -A cms multi restart ${CELERYD_NODES} --pidfile=${CELERYD_PID_FILE} --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS} -Q ${CELERY_QUEUE}'
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
13
deploy/local_install/dhparams.pem
Normal file
13
deploy/local_install/dhparams.pem
Normal file
@@ -0,0 +1,13 @@
|
||||
-----BEGIN DH PARAMETERS-----
|
||||
MIICCAKCAgEAo3MMiEY/fNbu+usIM0cDi6x8G3JBApv0Lswta4kiyedWT1WN51iQ
|
||||
9zhOFpmcu6517f/fR9MUdyhVKHxxSqWQTcmTEFtz4P3VLTS/W1N5VbKE2VEMLpIi
|
||||
wr350aGvV1Er0ujcp5n4O4h0I1tn4/fNyDe7+pHCdwM+hxe8hJ3T0/tKtad4fnIs
|
||||
WHDjl4f7m7KuFfheiK7Efb8MsT64HDDAYXn+INjtDZrbE5XPw20BqyWkrf07FcPx
|
||||
8o9GW50Ox7/FYq7jVMI/skEu0BRc8u6uUD9+UOuWUQpdeHeFcvLOgW53Z03XwWuX
|
||||
RXosUKzBPuGtUDAaKD/HsGW6xmGr2W9yRmu27jKpfYLUb/eWbbnRJwCw04LdzPqv
|
||||
jmtq02Gioo3lf5H5wYV9IYF6M8+q/slpbttsAcKERimD1273FBRt5VhSugkXWKjr
|
||||
XDhoXu6vZgj8Opei38qPa8pI1RUFoXHFlCe6WpZQmU8efL8gAMrJr9jUIY8eea1n
|
||||
u20t5B9ueb9JMjrNafcq6QkKhZLi6fRDDTUyeDvc0dN9R/3Yts97SXfdi1/lX7HS
|
||||
Ht4zXd5hEkvjo8GcnjsfZpAC39QfHWkDaeUGEqsl3jXjVMfkvoVY51OuokPWZzrJ
|
||||
M5+wyXNpfGbH67dPk7iHgN7VJvgX0SYscDPTtms50Vk7RwEzLeGuSHMCAQI=
|
||||
-----END DH PARAMETERS-----
|
||||
@@ -7,15 +7,15 @@ server {
|
||||
|
||||
error_log /var/log/nginx/mediacms.io.error.log warn;
|
||||
|
||||
# redirect to https if logged in
|
||||
if ($http_cookie ~* "sessionid") {
|
||||
rewrite ^/(.*)$ https://localhost/$1 permanent;
|
||||
}
|
||||
# # redirect to https if logged in
|
||||
# if ($http_cookie ~* "sessionid") {
|
||||
# rewrite ^/(.*)$ https://localhost/$1 permanent;
|
||||
# }
|
||||
|
||||
# redirect basic forms to https
|
||||
location ~ (login|login_form|register|mail_password_form)$ {
|
||||
rewrite ^/(.*)$ https://localhost/$1 permanent;
|
||||
}
|
||||
# # redirect basic forms to https
|
||||
# location ~ (login|login_form|register|mail_password_form)$ {
|
||||
# rewrite ^/(.*)$ https://localhost/$1 permanent;
|
||||
# }
|
||||
|
||||
location /static {
|
||||
alias /home/mediacms.io/mediacms/static ;
|
||||
@@ -46,6 +46,12 @@ server {
|
||||
|
||||
ssl_certificate_key /etc/letsencrypt/live/localhost/privkey.pem;
|
||||
ssl_certificate /etc/letsencrypt/live/localhost/fullchain.pem;
|
||||
ssl_dhparam /etc/nginx/dhparams/dhparams.pem;
|
||||
|
||||
ssl_protocols TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
|
||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
|
||||
ssl_ecdh_curve secp521r1:secp384r1;
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
gzip on;
|
||||
access_log /var/log/nginx/mediacms.io.access.log;
|
||||
@@ -59,7 +65,7 @@ server {
|
||||
location /media/original {
|
||||
alias /home/mediacms.io/mediacms/media_files/original;
|
||||
#auth_basic "auth protected area";
|
||||
#auth_basic_user_file /home/mediacms.io/mediacms/deploy/.htpasswd;
|
||||
#auth_basic_user_file /home/mediacms.io/mediacms/deploy/local_install/.htpasswd;
|
||||
}
|
||||
|
||||
location /media {
|
||||
@@ -2,7 +2,7 @@
|
||||
Description=MediaCMS uwsgi
|
||||
|
||||
[Service]
|
||||
ExecStart=/home/mediacms.io/bin/uwsgi --ini /home/mediacms.io/mediacms/uwsgi.ini
|
||||
ExecStart=/home/mediacms.io/bin/uwsgi --ini /home/mediacms.io/mediacms/deploy/local_install/uwsgi.ini
|
||||
ExecStop=/usr/bin/killall -9 uwsgi
|
||||
RestartSec=3
|
||||
#ExecRestart=killall -9 uwsgi; sleep 5; /home/sss/bin/uwsgi --ini /home/sss/wordgames/uwsgi.ini
|
||||
@@ -19,10 +19,7 @@ http {
|
||||
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
|
||||
ssl_prefer_server_ciphers on;
|
||||
|
||||
|
||||
access_log /var/log/nginx/access.log;
|
||||
error_log /var/log/nginx/error.log;
|
||||
|
||||
34
deploy/local_install/selinux-mediacms.te
Normal file
34
deploy/local_install/selinux-mediacms.te
Normal file
@@ -0,0 +1,34 @@
|
||||
module selinux-mediacms 1.0;
|
||||
|
||||
require {
|
||||
type init_t;
|
||||
type var_t;
|
||||
type redis_port_t;
|
||||
type postgresql_port_t;
|
||||
type httpd_t;
|
||||
type httpd_sys_content_t;
|
||||
type httpd_sys_rw_content_t;
|
||||
class file { append create execute execute_no_trans getattr ioctl lock open read rename setattr unlink write };
|
||||
class dir { add_name remove_name rmdir };
|
||||
class tcp_socket name_connect;
|
||||
class lnk_file read;
|
||||
}
|
||||
|
||||
#============= httpd_t ==============
|
||||
|
||||
allow httpd_t var_t:file { getattr open read };
|
||||
|
||||
#============= init_t ==============
|
||||
allow init_t postgresql_port_t:tcp_socket name_connect;
|
||||
|
||||
allow init_t redis_port_t:tcp_socket name_connect;
|
||||
|
||||
allow init_t httpd_sys_content_t:dir rmdir;
|
||||
|
||||
allow init_t httpd_sys_content_t:file { append create execute execute_no_trans ioctl lock open read rename setattr unlink write };
|
||||
|
||||
allow init_t httpd_sys_content_t:lnk_file read;
|
||||
|
||||
allow init_t httpd_sys_rw_content_t:dir { add_name remove_name rmdir };
|
||||
|
||||
allow init_t httpd_sys_rw_content_t:file { create ioctl lock open read setattr unlink write };
|
||||
@@ -1,8 +1,8 @@
|
||||
[uwsgi]
|
||||
|
||||
chdir = /home/mediacms.io/mediacms/
|
||||
chdir = /home/mediacms.io/mediacms/
|
||||
virtualenv = /home/mediacms.io
|
||||
module = cms.wsgi
|
||||
module = cms.wsgi
|
||||
|
||||
uid=www-data
|
||||
gid=www-data
|
||||
@@ -10,7 +10,7 @@ gid=www-data
|
||||
processes = 2
|
||||
threads = 2
|
||||
|
||||
master = true
|
||||
master = true
|
||||
|
||||
socket = 127.0.0.1:9000
|
||||
#socket = /home/mediacms.io/mediacms/deploy/uwsgi.sock
|
||||
@@ -19,9 +19,9 @@ socket = 127.0.0.1:9000
|
||||
workers = 2
|
||||
|
||||
|
||||
vacuum = true
|
||||
vacuum = true
|
||||
|
||||
logto = /home/mediacms.io/mediacms/logs/errorlog.txt
|
||||
|
||||
disable-logging = true
|
||||
|
||||
buffer-size=32768
|
||||
16
deploy/local_install/uwsgi_params
Normal file
16
deploy/local_install/uwsgi_params
Normal file
@@ -0,0 +1,16 @@
|
||||
uwsgi_param QUERY_STRING $query_string;
|
||||
uwsgi_param REQUEST_METHOD $request_method;
|
||||
uwsgi_param CONTENT_TYPE $content_type;
|
||||
uwsgi_param CONTENT_LENGTH $content_length;
|
||||
|
||||
uwsgi_param REQUEST_URI $request_uri;
|
||||
uwsgi_param PATH_INFO $document_uri;
|
||||
uwsgi_param DOCUMENT_ROOT $document_root;
|
||||
uwsgi_param SERVER_PROTOCOL $server_protocol;
|
||||
uwsgi_param REQUEST_SCHEME $scheme;
|
||||
uwsgi_param HTTPS $https if_not_empty;
|
||||
|
||||
uwsgi_param REMOTE_ADDR $remote_addr;
|
||||
uwsgi_param REMOTE_PORT $remote_port;
|
||||
uwsgi_param SERVER_PORT $server_port;
|
||||
uwsgi_param SERVER_NAME $server_name;
|
||||
40
deploy/scripts/build_and_deploy.sh
Normal file
40
deploy/scripts/build_and_deploy.sh
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
# This script builds the video editor package and deploys the frontend assets to the static directory.
|
||||
# How to run: sh deploy/scripts/build_and_deploy.sh
|
||||
|
||||
# Exit on any error
|
||||
set -e
|
||||
|
||||
echo "Starting build process..."
|
||||
|
||||
# Build video editor package
|
||||
echo "Building video editor package..."
|
||||
cd frontend-tools/video-editor
|
||||
yarn build:django
|
||||
cd ../../
|
||||
|
||||
# Build chapter editor package
|
||||
echo "Building chapters editor package..."
|
||||
cd frontend-tools/chapters-editor
|
||||
yarn build:django
|
||||
cd ../../
|
||||
|
||||
# Build video js package
|
||||
echo "Building video js package..."
|
||||
cd frontend-tools/video-js
|
||||
yarn build:django
|
||||
cd ../../
|
||||
|
||||
# Run npm build in the frontend container
|
||||
echo "Building frontend assets..."
|
||||
docker compose -f docker-compose/docker-compose-dev-updated.yaml exec frontend npm run dist
|
||||
|
||||
# Copy static assets to the static directory
|
||||
echo "Copying static assets..."
|
||||
cp -r frontend/dist/static/* static/
|
||||
|
||||
# Restart the web service
|
||||
echo "Restarting web service..."
|
||||
docker compose -f docker-compose/docker-compose-dev-updated.yaml restart web
|
||||
|
||||
echo "Build and deployment completed successfully!"
|
||||
@@ -1,19 +0,0 @@
|
||||
[uwsgi]
|
||||
|
||||
chdir = /home/mediacms.io/mediacms/
|
||||
virtualenv = /home/mediacms.io
|
||||
module = cms.wsgi
|
||||
|
||||
uid = www-data
|
||||
gid = www-data
|
||||
|
||||
processes = 10
|
||||
threads = 10
|
||||
master = true
|
||||
workers = 8
|
||||
vacuum = true
|
||||
|
||||
socket = 127.0.0.1:9000
|
||||
|
||||
logto = /home/mediacms.io/mediacms/logs/errorlog.txt
|
||||
|
||||
62
docker-compose-cert.yaml
Normal file
62
docker-compose-cert.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
version: "3.8"
|
||||
|
||||
# HTTPS/SSL certificate overlay for docker-compose.yaml
|
||||
# Uses nginx-proxy with Let's Encrypt via acme-companion
|
||||
#
|
||||
# Usage:
|
||||
# docker compose -f docker-compose.yaml -f docker-compose-cert.yaml up -d
|
||||
#
|
||||
# Before running:
|
||||
# 1. Change VIRTUAL_HOST to your domain
|
||||
# 2. Change LETSENCRYPT_HOST to your domain
|
||||
# 3. Change LETSENCRYPT_EMAIL to your email
|
||||
|
||||
services:
|
||||
# Reverse proxy with automatic SSL
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
container_name: nginx-proxy
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- conf:/etc/nginx/conf.d
|
||||
- vhost:/etc/nginx/vhost.d
|
||||
- html:/usr/share/nginx/html
|
||||
- dhparam:/etc/nginx/dhparam
|
||||
- certs:/etc/nginx/certs:ro
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./config/nginx-proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
|
||||
# Let's Encrypt certificate manager
|
||||
acme-companion:
|
||||
image: nginxproxy/acme-companion
|
||||
container_name: nginx-proxy-acme
|
||||
restart: unless-stopped
|
||||
volumes_from:
|
||||
- nginx-proxy
|
||||
volumes:
|
||||
- certs:/etc/nginx/certs:rw
|
||||
- acme:/etc/acme.sh
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
# Override nginx to work with nginx-proxy
|
||||
nginx:
|
||||
expose:
|
||||
- "80"
|
||||
ports: [] # Remove ports, nginx-proxy handles external access
|
||||
environment:
|
||||
# CHANGE THESE VALUES:
|
||||
VIRTUAL_HOST: 'mediacms.example.com'
|
||||
LETSENCRYPT_HOST: 'mediacms.example.com'
|
||||
LETSENCRYPT_EMAIL: 'admin@example.com'
|
||||
|
||||
volumes:
|
||||
# nginx-proxy volumes
|
||||
conf:
|
||||
vhost:
|
||||
html:
|
||||
dhparam:
|
||||
certs:
|
||||
acme:
|
||||
136
docker-compose-dev.yaml
Normal file
136
docker-compose-dev.yaml
Normal file
@@ -0,0 +1,136 @@
|
||||
version: "3.8"
|
||||
|
||||
# Development setup with hot-reload and file mounts
|
||||
# This is the ONLY compose file that mounts the source code
|
||||
|
||||
services:
|
||||
migrations:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: ./Dockerfile
|
||||
target: base
|
||||
args:
|
||||
- DEVELOPMENT_MODE=True
|
||||
image: mediacms/mediacms-dev:7.3
|
||||
command: ["/bin/bash", "/home/mediacms.io/mediacms/scripts/run-migrations.sh"]
|
||||
environment:
|
||||
DEVELOPMENT_MODE: 'True'
|
||||
DEBUG: 'True'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
restart: "no"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
|
||||
web:
|
||||
image: mediacms/mediacms-dev:7.3
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:8000"
|
||||
command: ["python", "manage.py", "runserver", "0.0.0.0:8000"]
|
||||
environment:
|
||||
DEVELOPMENT_MODE: 'True'
|
||||
DEBUG: 'True'
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
|
||||
frontend:
|
||||
image: node:20-alpine
|
||||
working_dir: /home/mediacms.io/mediacms/frontend/
|
||||
command: sh -c "npm install && npm run start"
|
||||
ports:
|
||||
- "8088:8088"
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
env_file:
|
||||
- ./frontend/.env
|
||||
volumes:
|
||||
- ./frontend:/home/mediacms.io/mediacms/frontend/
|
||||
depends_on:
|
||||
- web
|
||||
|
||||
celery_beat:
|
||||
image: mediacms/mediacms-dev:7.3
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "beat", "--loglevel=INFO"]
|
||||
environment:
|
||||
DEVELOPMENT_MODE: 'True'
|
||||
DEBUG: 'True'
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
|
||||
celery_short:
|
||||
image: mediacms/mediacms-dev:7.3
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "worker", "-Q", "short_tasks", "-c", "10", "--soft-time-limit=300", "--loglevel=INFO", "-n", "short@%h"]
|
||||
environment:
|
||||
DEVELOPMENT_MODE: 'True'
|
||||
DEBUG: 'True'
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
|
||||
celery_long:
|
||||
image: mediacms/mediacms-dev:7.3
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "worker", "-Q", "long_tasks", "-c", "1", "-Ofair", "--prefetch-multiplier=1", "--loglevel=INFO", "-n", "long@%h"]
|
||||
environment:
|
||||
DEVELOPMENT_MODE: 'True'
|
||||
DEBUG: 'True'
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
5
docker-compose.full.yaml
Normal file
5
docker-compose.full.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:full
|
||||
126
docker-compose.yaml
Normal file
126
docker-compose.yaml
Normal file
@@ -0,0 +1,126 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
migrations:
|
||||
image: mediacms/mediacms:7.3
|
||||
command: ["/bin/bash", "/home/mediacms.io/mediacms/scripts/run-migrations.sh"]
|
||||
environment:
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
ADMIN_PASSWORD: # ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
restart: "no"
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./custom:/home/mediacms.io/mediacms/custom:ro
|
||||
- static_files:/home/mediacms.io/mediacms/static
|
||||
- media_files:/home/mediacms.io/mediacms/media_files
|
||||
- logs:/home/mediacms.io/mediacms/logs
|
||||
|
||||
web:
|
||||
image: mediacms/mediacms:7.3
|
||||
restart: unless-stopped
|
||||
expose:
|
||||
- "9000"
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./custom:/home/mediacms.io/mediacms/custom:ro
|
||||
- static_files:/home/mediacms.io/mediacms/static
|
||||
- media_files:/home/mediacms.io/mediacms/media_files
|
||||
- logs:/home/mediacms.io/mediacms/logs
|
||||
|
||||
nginx:
|
||||
image: mediacms/mediacms-nginx:7.3
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
- web
|
||||
volumes:
|
||||
- ./custom/static:/var/www/custom:ro
|
||||
- static_files:/var/www/static:ro
|
||||
- media_files:/var/www/media:ro
|
||||
- logs:/var/log/mediacms
|
||||
|
||||
celery_beat:
|
||||
image: mediacms/mediacms-worker:7.3
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "beat", "--loglevel=INFO", "--schedule=/home/mediacms.io/mediacms/logs/celerybeat-schedule"]
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./custom:/home/mediacms.io/mediacms/custom:ro
|
||||
- media_files:/home/mediacms.io/mediacms/media_files
|
||||
- logs:/home/mediacms.io/mediacms/logs
|
||||
|
||||
celery_short:
|
||||
image: mediacms/mediacms-worker:7.3
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "worker", "-Q", "short_tasks", "-c", "10", "--soft-time-limit=300", "--loglevel=INFO", "-n", "short@%h"]
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./custom:/home/mediacms.io/mediacms/custom:ro
|
||||
- media_files:/home/mediacms.io/mediacms/media_files
|
||||
- logs:/home/mediacms.io/mediacms/logs
|
||||
|
||||
celery_long:
|
||||
image: mediacms/mediacms-worker:7.3
|
||||
# To use extra codecs, change image to: mediacms/mediacms-worker:7.3-full
|
||||
restart: unless-stopped
|
||||
command: ["/home/mediacms.io/bin/celery", "-A", "cms", "worker", "-Q", "long_tasks", "-c", "1", "-Ofair", "--prefetch-multiplier=1", "--loglevel=INFO", "-n", "long@%h"]
|
||||
depends_on:
|
||||
migrations:
|
||||
condition: service_completed_successfully
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./custom:/home/mediacms.io/mediacms/custom:ro
|
||||
- media_files:/home/mediacms.io/mediacms/media_files
|
||||
- logs:/home/mediacms.io/mediacms/logs
|
||||
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:alpine
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
static_files:
|
||||
media_files:
|
||||
logs:
|
||||
124
docker-compose/docker-compose-dev-updated.yaml
Normal file
124
docker-compose/docker-compose-dev-updated.yaml
Normal file
@@ -0,0 +1,124 @@
|
||||
name: mediacms-dev
|
||||
services:
|
||||
migrations:
|
||||
platform: linux/amd64
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
- DEVELOPMENT_MODE=True
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ../:/home/mediacms.io/mediacms/
|
||||
command: "/home/mediacms.io/mediacms/deploy/docker/prestart.sh"
|
||||
environment:
|
||||
DEVELOPMENT_MODE: True
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
frontend:
|
||||
image: node:20
|
||||
user: "root"
|
||||
volumes:
|
||||
- ${PWD}/frontend:/home/mediacms.io/mediacms/frontend/
|
||||
- frontend_node_modules:/home/mediacms.io/mediacms/frontend/node_modules
|
||||
- scripts_node_modules:/home/mediacms.io/mediacms/frontend/packages/scripts/node_modules
|
||||
- npm_cache:/home/node/.npm
|
||||
working_dir: /home/mediacms.io/mediacms/frontend/
|
||||
command: >
|
||||
bash -c "
|
||||
echo 'Checking dependencies...' &&
|
||||
if [ ! -f node_modules/.install-complete ]; then
|
||||
echo 'First-time setup or dependencies changed, installing...' &&
|
||||
npm install --legacy-peer-deps --cache /home/node/.npm &&
|
||||
cd packages/scripts &&
|
||||
npm install --legacy-peer-deps --cache /home/node/.npm &&
|
||||
npm run build &&
|
||||
cd ../.. &&
|
||||
touch node_modules/.install-complete &&
|
||||
echo 'Dependencies installed successfully'
|
||||
else
|
||||
echo 'Dependencies already installed, skipping installation...' &&
|
||||
if [ ! -d packages/scripts/dist ]; then
|
||||
echo 'Building scripts package...' &&
|
||||
cd packages/scripts &&
|
||||
npm run build &&
|
||||
cd ../..
|
||||
fi
|
||||
fi &&
|
||||
echo 'Starting development server...' &&
|
||||
npm run start
|
||||
"
|
||||
env_file:
|
||||
- ${PWD}/frontend/.env
|
||||
ports:
|
||||
- "8088:8088"
|
||||
depends_on:
|
||||
- web
|
||||
restart: unless-stopped
|
||||
web:
|
||||
platform: linux/amd64
|
||||
image: mediacms/mediacms:latest
|
||||
command: "python manage.py runserver 0.0.0.0:80"
|
||||
environment:
|
||||
DEVELOPMENT_MODE: True
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- ../:/home/mediacms.io/mediacms/
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
celery_worker:
|
||||
platform: linux/amd64
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ../:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
DEVELOPMENT_MODE: True
|
||||
depends_on:
|
||||
- web
|
||||
|
||||
volumes:
|
||||
frontend_node_modules:
|
||||
scripts_node_modules:
|
||||
npm_cache:
|
||||
92
docker-compose/docker-compose-http-proxy.yaml
Normal file
92
docker-compose/docker-compose-http-proxy.yaml
Normal file
@@ -0,0 +1,92 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: jwilder/nginx-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
VIRTUAL_HOST: localhost
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data/:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
94
docker-compose/docker-compose-https-proxy.yaml
Normal file
94
docker-compose/docker-compose-https-proxy.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: jwilder/nginx-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./deploy/docker/reverse_proxy/certs/:/etc/nginx/certs/
|
||||
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
VIRTUAL_HOST: localhost
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 2
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data/:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
121
docker-compose/docker-compose-letsencrypt.yaml
Normal file
121
docker-compose/docker-compose-letsencrypt.yaml
Normal file
@@ -0,0 +1,121 @@
|
||||
version: "3"
|
||||
|
||||
# Uses https://github.com/nginx-proxy/acme-companion
|
||||
|
||||
services:
|
||||
nginx-proxy:
|
||||
image: nginxproxy/nginx-proxy
|
||||
container_name: nginx-proxy
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- conf:/etc/nginx/conf.d
|
||||
- vhost:/etc/nginx/vhost.d
|
||||
- html:/usr/share/nginx/html
|
||||
- dhparam:/etc/nginx/dhparam
|
||||
- certs:/etc/nginx/certs:ro
|
||||
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||
- ./deploy/docker/reverse_proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
|
||||
acme-companion:
|
||||
image: nginxproxy/acme-companion
|
||||
container_name: nginx-proxy-acme
|
||||
volumes_from:
|
||||
- nginx-proxy
|
||||
volumes:
|
||||
- certs:/etc/nginx/certs:rw
|
||||
- acme:/etc/acme.sh
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
VIRTUAL_HOST: 'mediacms.52.209.5.113.nip.io'
|
||||
LETSENCRYPT_HOST: 'mediacms.52.209.5.113.nip.io'
|
||||
LETSENCRYPT_EMAIL: 'email@example.com'
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- ./:/home/mediacms.io/mediacms/
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- ../postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
volumes:
|
||||
conf:
|
||||
vhost:
|
||||
html:
|
||||
dhparam:
|
||||
certs:
|
||||
acme:
|
||||
94
docker-compose/docker-compose-named-volumes.yaml
Normal file
94
docker-compose/docker-compose-named-volumes.yaml
Normal file
@@ -0,0 +1,94 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
migrations:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
#ADMIN_PASSWORD: 'uncomment_and_set_password_here'
|
||||
command: "./deploy/docker/prestart.sh"
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
db:
|
||||
condition: service_healthy
|
||||
web:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
ports:
|
||||
- "80:80"
|
||||
volumes:
|
||||
- media_store:/home/mediacms.io/mediacms/media_files/
|
||||
- static_store:/home/mediacms.io/mediacms/static/
|
||||
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
|
||||
environment:
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
celery_beat:
|
||||
image: mediacms/mediacms:latest
|
||||
volumes:
|
||||
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_SHORT: 'no'
|
||||
ENABLE_CELERY_LONG: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- redis
|
||||
celery_worker:
|
||||
image: mediacms/mediacms:latest
|
||||
deploy:
|
||||
replicas: 1
|
||||
volumes:
|
||||
- media_store:/home/mediacms.io/mediacms/media_files/
|
||||
- static_store:/home/mediacms.io/mediacms/static/
|
||||
- ./deploy/docker/local_settings.py:/home/mediacms.io/mediacms/deploy/docker/local_settings.py
|
||||
environment:
|
||||
ENABLE_UWSGI: 'no'
|
||||
ENABLE_NGINX: 'no'
|
||||
ENABLE_CELERY_BEAT: 'no'
|
||||
ENABLE_MIGRATIONS: 'no'
|
||||
depends_on:
|
||||
- migrations
|
||||
db:
|
||||
image: postgres:17.2-alpine
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_USER: mediacms
|
||||
POSTGRES_PASSWORD: mediacms
|
||||
POSTGRES_DB: mediacms
|
||||
TZ: Europe/London
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}", "--host=db", "--dbname=$POSTGRES_DB", "--username=$POSTGRES_USER"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
redis:
|
||||
image: "redis:alpine"
|
||||
restart: always
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli","ping"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
volumes:
|
||||
postgres_data:
|
||||
media_store:
|
||||
static_store:
|
||||
@@ -1,244 +0,0 @@
|
||||
## Configuration
|
||||
|
||||
A number of options are available on `cms/settings.py`.
|
||||
|
||||
It is advisable to override any of them by adding it to `cms/local_settings.py` .
|
||||
|
||||
Any change needs restart of MediaCMS in order to take effect. So edit `cms/local_settings.py`, make a change and restart MediaCMS
|
||||
|
||||
```
|
||||
#systemctl restart mediacms
|
||||
```
|
||||
|
||||
|
||||
### change portal logo
|
||||
|
||||
Set a new svg file for the white theme (`static/images/logo_dark.svg`) or the dark theme (`static/images/logo_light.svg`)
|
||||
|
||||
### set global portal title
|
||||
|
||||
set `PORTAL_NAME`, eg
|
||||
|
||||
```
|
||||
PORTAL_NAME = 'my awesome portal'
|
||||
```
|
||||
|
||||
### who can add media
|
||||
|
||||
By default `CAN_ADD_MEDIA = "all"` means that all registered users can add media. Other valid options are:
|
||||
|
||||
- **email_verified**, a user not only has to register an account but also verify the email (by clicking the link sent upon registration). Apparently email configuration need to work, otherise users won't receive emails.
|
||||
|
||||
- **advancedUser**, only users that are marked as advanced users can add media. Admins or MediaCMS managers can make users advanced users by editing their profile and selecting advancedUser.
|
||||
|
||||
### what is the portal workflow
|
||||
|
||||
The `PORTAL_WORKFLOW` variable specifies what happens to newly uploaded media, whether they appear on listings (as the index page, or search)
|
||||
|
||||
- **public** is the default option and means that a media can appear on listings. If media type is video, it will appear once at least a task that produces an encoded version of the file has finished succesfully. For other type of files, as image/audio they appear instantly
|
||||
|
||||
- **private** means that newly uploaded content is private - only users can see it or MediaCMS editors, managers and admins. Those can also set the status to public or unlisted
|
||||
|
||||
- **unlisted** means that items are unlisted. However if a user visits the url of an unlisted media, it will be shown (as opposed to private)
|
||||
|
||||
|
||||
### show/hide the Sign in button
|
||||
|
||||
to show button:
|
||||
```
|
||||
LOGIN_ALLOWED = True
|
||||
```
|
||||
|
||||
to hide button:
|
||||
|
||||
```
|
||||
LOGIN_ALLOWED = False
|
||||
```
|
||||
|
||||
### show/hide the Register button
|
||||
|
||||
to show button:
|
||||
```
|
||||
REGISTER_ALLOWED = True
|
||||
```
|
||||
|
||||
to hide button:
|
||||
|
||||
```
|
||||
REGISTER_ALLOWED = False
|
||||
```
|
||||
|
||||
|
||||
### show/hide the upload media button
|
||||
|
||||
To show:
|
||||
|
||||
```
|
||||
UPLOAD_MEDIA_ALLOWED = True
|
||||
```
|
||||
|
||||
To hide:
|
||||
|
||||
```
|
||||
UPLOAD_MEDIA_ALLOWED = False
|
||||
```
|
||||
|
||||
### show/hide the actions buttons (like/dislike/report)
|
||||
|
||||
Make changes (True/False) to any of the following:
|
||||
|
||||
```
|
||||
- CAN_LIKE_MEDIA = True # whether the like media appears
|
||||
- CAN_DISLIKE_MEDIA = True # whether the dislike media appears
|
||||
- CAN_REPORT_MEDIA = True # whether the report media appears
|
||||
- CAN_SHARE_MEDIA = True # whether the share media appears
|
||||
```
|
||||
|
||||
### automatically hide media upon being reported
|
||||
|
||||
set a low number for variable `REPORTED_TIMES_THRESHOLD`
|
||||
eg
|
||||
|
||||
```
|
||||
REPORTED_TIMES_THRESHOLD = 2
|
||||
```
|
||||
|
||||
once the limit is reached, media goes to private state and an email is sent to admins
|
||||
|
||||
### set a custom message on the media upload page
|
||||
|
||||
this message will appear below the media drag and drop form
|
||||
|
||||
```
|
||||
PRE_UPLOAD_MEDIA_MESSAGE = 'custom message'
|
||||
```
|
||||
|
||||
### set email settings
|
||||
|
||||
Set correct settings per provider
|
||||
|
||||
```
|
||||
DEFAULT_FROM_EMAIL = 'info@mediacms.io'
|
||||
EMAIL_HOST_PASSWORD = 'xyz'
|
||||
EMAIL_HOST_USER = 'info@mediacms.io'
|
||||
EMAIL_USE_TLS = True
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
||||
EMAIL_HOST = 'mediacms.io'
|
||||
EMAIL_PORT = 587
|
||||
ADMIN_EMAIL_LIST = ['info@mediacms.io']
|
||||
```
|
||||
|
||||
### disallow user registrations from specific domains
|
||||
|
||||
set domains that are not valid for registration via this variable:
|
||||
|
||||
```
|
||||
RESTRICTED_DOMAINS_FOR_USER_REGISTRATION = [
|
||||
'xxx.com', 'emaildomainwhatever.com']
|
||||
```
|
||||
|
||||
### require a review by MediaCMS editors/managers/admins
|
||||
|
||||
set value
|
||||
|
||||
```
|
||||
MEDIA_IS_REVIEWED = False
|
||||
```
|
||||
|
||||
any uploaded media now needs to be reviewed before it can appear to the listings.
|
||||
MediaCMS editors/managers/admins can visit the media page and edit it, where they can see the option to mark media as reviewed. By default this is set to True, so all media don't require to be reviewed
|
||||
|
||||
### specify maximum number of media for a playlist
|
||||
|
||||
set a different threshold on variable `MAX_MEDIA_PER_PLAYLIST`
|
||||
|
||||
eg
|
||||
|
||||
```
|
||||
MAX_MEDIA_PER_PLAYLIST = 14
|
||||
```
|
||||
|
||||
### specify maximum size of a media that can be uploaded
|
||||
|
||||
change `UPLOAD_MAX_SIZE`.
|
||||
|
||||
default is 4GB
|
||||
|
||||
```
|
||||
UPLOAD_MAX_SIZE = 800 * 1024 * 1000 * 5
|
||||
```
|
||||
|
||||
### specify maximum size of comments
|
||||
|
||||
change `MAX_CHARS_FOR_COMMENT`
|
||||
|
||||
default:
|
||||
|
||||
```
|
||||
MAX_CHARS_FOR_COMMENT = 10000
|
||||
```
|
||||
|
||||
### how many files to upload in parallel
|
||||
|
||||
set a different threshold for `UPLOAD_MAX_FILES_NUMBER`
|
||||
default:
|
||||
|
||||
```
|
||||
UPLOAD_MAX_FILES_NUMBER = 100
|
||||
```
|
||||
|
||||
### force users confirm their email upon registrations
|
||||
|
||||
default option for email confirmation is optional. Set this to mandatory in order to force users confirm their email before they can login
|
||||
|
||||
```
|
||||
ACCOUNT_EMAIL_VERIFICATION = 'optional'
|
||||
```
|
||||
|
||||
### rate limit account login attempts
|
||||
|
||||
after this number is reached
|
||||
|
||||
```
|
||||
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = 20
|
||||
```
|
||||
|
||||
sets a timeout (in seconds)
|
||||
|
||||
```
|
||||
ACCOUNT_LOGIN_ATTEMPTS_TIMEOUT = 5
|
||||
```
|
||||
|
||||
### disallow user registration
|
||||
|
||||
set the following variable to False
|
||||
|
||||
```
|
||||
USERS_CAN_SELF_REGISTER = True
|
||||
```
|
||||
|
||||
### configure notifications
|
||||
|
||||
Global notifications that are implemented are controlled by the following options:
|
||||
|
||||
```
|
||||
USERS_NOTIFICATIONS = {
|
||||
'MEDIA_ADDED': True,
|
||||
}
|
||||
```
|
||||
|
||||
If you want to disable notification for new media, set to False
|
||||
|
||||
Admins also receive notifications on different events, set any of the following to False to disable
|
||||
|
||||
```
|
||||
ADMINS_NOTIFICATIONS = {
|
||||
'NEW_USER': True,
|
||||
'MEDIA_ADDED': True,
|
||||
'MEDIA_REPORTED': True,
|
||||
}
|
||||
```
|
||||
|
||||
- NEW_USER: a new user is added
|
||||
- MEDIA_ADDED: a media is added
|
||||
- MEDIA_REPORTED: the report for a media was hit
|
||||
367
docs/DOCKER_V7.3_MIGRATION.md
Normal file
367
docs/DOCKER_V7.3_MIGRATION.md
Normal file
@@ -0,0 +1,367 @@
|
||||
# MediaCMS 7.3 Docker Architecture Migration Guide
|
||||
|
||||
## Overview
|
||||
|
||||
MediaCMS 7.3 introduces a modernized Docker architecture that removes supervisord and implements Docker best practices with one process per container.
|
||||
|
||||
## What Changed
|
||||
|
||||
### Old Architecture (pre-7.3)
|
||||
- Single multi-purpose image with supervisord
|
||||
- Environment variables (`ENABLE_UWSGI`, `ENABLE_NGINX`, etc.) to control services
|
||||
- All services bundled in `deploy/docker/` folder
|
||||
- File mounts required for all deployments
|
||||
|
||||
### New Architecture (7.3+)
|
||||
- **Dedicated images** for each service:
|
||||
- `mediacms/mediacms:7.3` - Django/uWSGI application
|
||||
- `mediacms/mediacms-worker:7.3` - Celery workers
|
||||
- `mediacms/mediacms-worker:7.3-full` - Celery workers with extra codecs
|
||||
- `mediacms/mediacms-nginx:7.3` - Nginx web server
|
||||
- **No supervisord** - Native Docker process management
|
||||
- **Separated services**:
|
||||
- `migrations` - Runs database migrations on every startup
|
||||
- `nginx` - Serves static/media files and proxies to Django
|
||||
- `web` - Django application (uWSGI)
|
||||
- `celery_short` - Short-running tasks (thumbnails, etc.)
|
||||
- `celery_long` - Long-running tasks (video encoding)
|
||||
- `celery_beat` - Task scheduler
|
||||
- **No ENABLE_* environment variables**
|
||||
- **Config centralized** in `config/` directory
|
||||
- **File mounts only for development** (`docker-compose-dev.yaml`)
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```
|
||||
config/
|
||||
├── nginx/
|
||||
│ ├── nginx.conf # Main nginx config
|
||||
│ ├── site.conf # Virtual host config
|
||||
│ └── uwsgi_params # uWSGI parameters
|
||||
├── nginx-proxy/
|
||||
│ └── client_max_body_size.conf # For production HTTPS proxy
|
||||
├── uwsgi/
|
||||
│ └── uwsgi.ini # uWSGI configuration
|
||||
└── imagemagick/
|
||||
└── policy.xml # ImageMagick policy
|
||||
|
||||
scripts/
|
||||
├── entrypoint-web.sh # Web container entrypoint
|
||||
├── entrypoint-worker.sh # Worker container entrypoint
|
||||
└── run-migrations.sh # Migration script
|
||||
|
||||
Dockerfile.new # Main Dockerfile (base, web, worker, worker-full)
|
||||
Dockerfile.nginx # Nginx Dockerfile
|
||||
docker-compose.yaml # Production deployment
|
||||
docker-compose-cert.yaml # Production with HTTPS
|
||||
docker-compose-dev.yaml # Development with file mounts
|
||||
```
|
||||
|
||||
## Migration Steps
|
||||
|
||||
### For Existing Production Systems
|
||||
|
||||
#### Step 1: Backup your data
|
||||
```bash
|
||||
# Backup database
|
||||
docker exec mediacms_db_1 pg_dump -U mediacms mediacms > backup.sql
|
||||
|
||||
# Backup media files
|
||||
cp -r media_files media_files.backup
|
||||
```
|
||||
|
||||
#### Step 2: Update configuration location
|
||||
```bash
|
||||
# The client_max_body_size.conf has moved
|
||||
# No action needed if you haven't customized it
|
||||
```
|
||||
|
||||
#### Step 3: Pull latest images
|
||||
```bash
|
||||
docker pull mediacms/mediacms:7.3
|
||||
docker pull mediacms/mediacms-worker:7.3
|
||||
docker pull mediacms/mediacms-nginx:7.3
|
||||
```
|
||||
|
||||
#### Step 4: Update docker-compose file
|
||||
If using **docker-compose.yaml**:
|
||||
- No changes needed, just use the new version
|
||||
|
||||
If using **docker-compose-cert.yaml** (HTTPS):
|
||||
- Update `VIRTUAL_HOST`, `LETSENCRYPT_HOST`, and `LETSENCRYPT_EMAIL` in the nginx service
|
||||
- Update the path to client_max_body_size.conf:
|
||||
```yaml
|
||||
- ./config/nginx-proxy/client_max_body_size.conf:/etc/nginx/conf.d/client_max_body_size.conf:ro
|
||||
```
|
||||
|
||||
#### Step 5: Restart services
|
||||
```bash
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### For Development Systems
|
||||
|
||||
Development now requires the `-dev` compose file:
|
||||
|
||||
```bash
|
||||
# Old way (no longer works)
|
||||
docker compose up
|
||||
|
||||
# New way (development)
|
||||
docker compose -f docker-compose-dev.yaml up
|
||||
```
|
||||
|
||||
## Deployment Options
|
||||
|
||||
### Standard Deployment (HTTP)
|
||||
|
||||
**File**: `docker-compose.yaml`
|
||||
|
||||
**Command**:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Self-contained images (no file mounts)
|
||||
- Nginx serves on port 80
|
||||
- Separate containers for each service
|
||||
- Named volumes for persistence
|
||||
|
||||
**Architecture**:
|
||||
```
|
||||
Client → nginx:80 → web:9000 (uWSGI)
|
||||
↓
|
||||
static_files (volume)
|
||||
media_files (volume)
|
||||
```
|
||||
|
||||
### Production Deployment (HTTPS with Let's Encrypt)
|
||||
|
||||
**File**: `docker-compose-cert.yaml`
|
||||
|
||||
**Prerequisites**:
|
||||
1. Domain name pointing to your server
|
||||
2. Ports 80 and 443 open
|
||||
|
||||
**Setup**:
|
||||
```bash
|
||||
# 1. Edit docker-compose-cert.yaml
|
||||
# Update these values in the nginx service:
|
||||
# VIRTUAL_HOST: 'your-domain.com'
|
||||
# LETSENCRYPT_HOST: 'your-domain.com'
|
||||
# LETSENCRYPT_EMAIL: 'your-email@example.com'
|
||||
|
||||
# 2. Start services
|
||||
docker compose -f docker-compose-cert.yaml up -d
|
||||
|
||||
# 3. Check logs
|
||||
docker compose -f docker-compose-cert.yaml logs -f nginx-proxy acme-companion
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Automatic HTTPS via Let's Encrypt
|
||||
- Certificate auto-renewal
|
||||
- Reverse proxy handles SSL termination
|
||||
|
||||
**Architecture**:
|
||||
```
|
||||
Client → nginx-proxy:443 (HTTPS) → nginx:80 → web:9000 (uWSGI)
|
||||
```
|
||||
|
||||
### Development Deployment
|
||||
|
||||
**File**: `docker-compose-dev.yaml`
|
||||
|
||||
**Command**:
|
||||
```bash
|
||||
docker compose -f docker-compose-dev.yaml up
|
||||
```
|
||||
|
||||
**Features**:
|
||||
- Source code mounted for live editing
|
||||
- Django debug mode enabled
|
||||
- Django's `runserver` instead of uWSGI
|
||||
- Frontend hot-reload on port 8088
|
||||
- No nginx (direct Django access on port 80)
|
||||
|
||||
**Ports**:
|
||||
- `80` - Django API
|
||||
- `8088` - Frontend dev server
|
||||
|
||||
## Configuration
|
||||
|
||||
### Environment Variables
|
||||
|
||||
All configuration is done via environment variables or `cms/local_settings.py`.
|
||||
|
||||
**Key Variables**:
|
||||
- `FRONTEND_HOST` - Your domain (e.g., `https://mediacms.example.com`)
|
||||
- `PORTAL_NAME` - Your portal name
|
||||
- `SECRET_KEY` - Django secret key
|
||||
- `POSTGRES_*` - Database credentials
|
||||
- `REDIS_LOCATION` - Redis connection string
|
||||
- `DEBUG` - Enable debug mode (development only)
|
||||
|
||||
**Setting variables**:
|
||||
|
||||
Option 1: In docker-compose file:
|
||||
```yaml
|
||||
environment:
|
||||
FRONTEND_HOST: 'https://mediacms.example.com'
|
||||
PORTAL_NAME: 'My MediaCMS'
|
||||
```
|
||||
|
||||
Option 2: Using .env file (recommended):
|
||||
```bash
|
||||
# Create .env file
|
||||
cat > .env << EOF
|
||||
FRONTEND_HOST=https://mediacms.example.com
|
||||
PORTAL_NAME=My MediaCMS
|
||||
SECRET_KEY=your-secret-key-here
|
||||
EOF
|
||||
```
|
||||
|
||||
### Customizing Settings
|
||||
|
||||
For advanced customization, you can build a custom image:
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile.custom
|
||||
FROM mediacms/mediacms:7.3
|
||||
COPY my_local_settings.py /home/mediacms.io/mediacms/cms/local_settings.py
|
||||
```
|
||||
|
||||
## Celery Workers
|
||||
|
||||
### Standard Workers
|
||||
|
||||
By default, `celery_long` uses the standard image:
|
||||
```yaml
|
||||
celery_long:
|
||||
image: mediacms/mediacms-worker:7.3
|
||||
```
|
||||
|
||||
### Full Workers (Extra Codecs)
|
||||
|
||||
To enable extra codecs for better transcoding (including Whisper for subtitles):
|
||||
|
||||
**Edit docker-compose file**:
|
||||
```yaml
|
||||
celery_long:
|
||||
image: mediacms/mediacms-worker:7.3-full # Changed from :7.3
|
||||
```
|
||||
|
||||
**Then restart**:
|
||||
```bash
|
||||
docker compose up -d celery_long
|
||||
```
|
||||
|
||||
### Scaling Workers
|
||||
|
||||
You can scale workers independently:
|
||||
|
||||
```bash
|
||||
# Scale short task workers
|
||||
docker compose up -d --scale celery_short=3
|
||||
|
||||
# Scale long task workers
|
||||
docker compose up -d --scale celery_long=2
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Migrations not running
|
||||
```bash
|
||||
# Check migrations container logs
|
||||
docker compose logs migrations
|
||||
|
||||
# Manually run migrations
|
||||
docker compose run --rm migrations
|
||||
```
|
||||
|
||||
### Static files not loading
|
||||
```bash
|
||||
# Ensure migrations completed (it runs collectstatic)
|
||||
docker compose logs migrations
|
||||
|
||||
# Check nginx can access volumes
|
||||
docker compose exec nginx ls -la /var/www/static
|
||||
```
|
||||
|
||||
### Permission issues
|
||||
```bash
|
||||
# Check volume ownership
|
||||
docker compose exec web ls -la /home/mediacms.io/mediacms/media_files
|
||||
|
||||
# If needed, rebuild images
|
||||
docker compose build --no-cache
|
||||
```
|
||||
|
||||
### Celery workers not processing tasks
|
||||
```bash
|
||||
# Check worker logs
|
||||
docker compose logs celery_short celery_long
|
||||
|
||||
# Check Redis connection
|
||||
docker compose exec redis redis-cli ping
|
||||
|
||||
# Restart workers
|
||||
docker compose restart celery_short celery_long celery_beat
|
||||
```
|
||||
|
||||
## Removed Components
|
||||
|
||||
The following are **no longer used** in 7.3:
|
||||
|
||||
- ❌ `deploy/docker/supervisord/` - Supervisord configs
|
||||
- ❌ `deploy/docker/start.sh` - Start script
|
||||
- ❌ `deploy/docker/entrypoint.sh` - Old entrypoint
|
||||
- ❌ Environment variables: `ENABLE_UWSGI`, `ENABLE_NGINX`, `ENABLE_CELERY_BEAT`, `ENABLE_CELERY_SHORT`, `ENABLE_CELERY_LONG`, `ENABLE_MIGRATIONS`
|
||||
|
||||
**These are still available but moved**:
|
||||
- ✅ `config/nginx/` - Nginx configs (moved from `deploy/docker/`)
|
||||
- ✅ `config/uwsgi/` - uWSGI config (moved from `deploy/docker/`)
|
||||
- ✅ `config/nginx-proxy/` - Reverse proxy config (moved from `deploy/docker/reverse_proxy/`)
|
||||
|
||||
## Persistent Volumes
|
||||
|
||||
MediaCMS 7.3 uses Docker named volumes for data persistence:
|
||||
|
||||
- **`media_files`** - All uploaded media (videos, images, thumbnails, HLS streams)
|
||||
- Mounted on: migrations, web, nginx, celery_beat, celery_short, celery_long
|
||||
- Persists across container restarts, updates, and image removals
|
||||
|
||||
- **`logs`** - Application and nginx logs
|
||||
- Mounted on: migrations, web, nginx, celery_beat, celery_short, celery_long
|
||||
- Nginx logs: `/var/log/mediacms/nginx.access.log`, `/var/log/mediacms/nginx.error.log`
|
||||
- Django/Celery logs: `/home/mediacms.io/mediacms/logs/`
|
||||
- Persists across container restarts, updates, and image removals
|
||||
|
||||
- **`static_files`** - Django static files (CSS, JS, images)
|
||||
- Mounted on: migrations, web, nginx
|
||||
- Regenerated during migrations via `collectstatic`
|
||||
|
||||
- **`postgres_data`** - PostgreSQL database
|
||||
- Mounted on: db
|
||||
- Persists across container restarts, updates, and image removals
|
||||
|
||||
**Important**: Use `docker compose down -v` to remove volumes (⚠️ causes data loss!)
|
||||
|
||||
## Benefits of New Architecture
|
||||
|
||||
1. **Better resource management** - Scale services independently
|
||||
2. **Easier debugging** - Clear separation of concerns
|
||||
3. **Faster restarts** - Restart only affected services
|
||||
4. **Production-ready** - No file mounts, immutable images
|
||||
5. **Standard Docker practices** - One process per container
|
||||
6. **Clearer logs** - Each service has isolated logs, persistent storage
|
||||
7. **Better health checks** - Per-service monitoring
|
||||
8. **Data persistence** - media_files and logs survive all container operations
|
||||
|
||||
## Support
|
||||
|
||||
For issues or questions:
|
||||
- GitHub Issues: https://github.com/mediacms-io/mediacms/issues
|
||||
- Documentation: https://docs.mediacms.io
|
||||
@@ -1,20 +0,0 @@
|
||||
## User scenarios to test
|
||||
|
||||
|
||||
## test video media + image
|
||||
try uploading a video + image, make sure they get encoded well and check they appear on index/search/category/author page
|
||||
try editing/setting metadata, confirm action is performed, also that are searchable
|
||||
try adding custom poster, confirm it loads well on video page/listings
|
||||
try specifying different thumbnail time, confirm an automatic screenshot is taken
|
||||
|
||||
|
||||
## portal workflow
|
||||
change workflow to unlisted, check they don't appear on index/search/category/author page
|
||||
|
||||
## users management
|
||||
create an admin, a MediaCMS editor and MediaCMS manager. All should see edit/delete on a media and also comments, and action should work.
|
||||
For users edit and delete, only MediaCMS manager and admin should see edit/delete and these actions should work.
|
||||
|
||||
## test subtitle
|
||||
add language and test subtitling
|
||||
|
||||
1160
docs/admins_docs.md
Normal file
1160
docs/admins_docs.md
Normal file
File diff suppressed because it is too large
Load Diff
89
docs/dev_exp.md
Normal file
89
docs/dev_exp.md
Normal file
@@ -0,0 +1,89 @@
|
||||
# Developer Experience
|
||||
There is ongoing effort to provide a better developer experience and document it.
|
||||
|
||||
## How to develop locally with Docker
|
||||
First install a recent version of [Docker](https://docs.docker.com/get-docker/), and [Docker Compose](https://docs.docker.com/compose/install/).
|
||||
|
||||
Then run `docker compose -f docker-compose-dev.yaml up`
|
||||
|
||||
```
|
||||
user@user:~/mediacms$ docker compose -f docker-compose-dev.yaml up
|
||||
```
|
||||
|
||||
In a few minutes the app will be available at http://localhost . Login via admin/admin
|
||||
|
||||
### What does docker-compose-dev.yaml do?
|
||||
It build the two images used for backend and frontend.
|
||||
|
||||
* Backend: `mediacms/mediacms-dev:latest`
|
||||
* Frontend: `frontend`
|
||||
|
||||
and will start all services required for MediaCMS, as Celery/Redis for asynchronous tasks, PostgreSQL database, Django and React
|
||||
|
||||
For Django, the changes from the image produced by docker-compose.yaml are these:
|
||||
|
||||
* Django runs in debug mode, with `python manage.py runserver`
|
||||
* uwsgi and nginx are not run
|
||||
* Django runs in Debug mode, with Debug Toolbar
|
||||
* Static files (js/css) are loaded from static/ folder
|
||||
* corsheaders is installed and configured to allow all origins
|
||||
|
||||
For React, it will run `npm start` in the frontend folder, which will start the development server.
|
||||
Check it on http://localhost:8088/
|
||||
|
||||
### How to develop in Django
|
||||
Django starts at http://localhost and is reloading automatically. Making any change to the python code should refresh Django.
|
||||
|
||||
If Django breaks due to an error (eg SyntaxError, while editing the code), you might have to restart it
|
||||
|
||||
```
|
||||
docker compose -f docker-compose-dev.yaml restart web
|
||||
```
|
||||
|
||||
|
||||
|
||||
### How to develop in React
|
||||
React is started on http://localhost:8088/ , code is located in frontend/ , so making changes there should have instant effect on the page. Keep in mind that React is loading data from Django, and that it has to be built so that Django can serve it.
|
||||
|
||||
### Making changes to the frontend
|
||||
|
||||
The way React is added is more complicated than the usual SPA project and this is because React is used as a library loaded by Django Templates, so it is not a standalone project and is not handling routes etc.
|
||||
|
||||
The two directories to consider are:
|
||||
* frontend/src , for the React files
|
||||
* templates/, for the Django templates.
|
||||
|
||||
Django is using a highly intuitive hierarchical templating system (https://docs.djangoproject.com/en/4.2/ref/templates/), where the base template is templates/root.html and all other templates are extending it.
|
||||
|
||||
React is called through the Django templates, eg templates/cms/media.html is loading js/media.js
|
||||
|
||||
In order to make changes to React code, edit code on frontend/src and check it's effect on http://localhost:8088/ . Once ready, build it and copy it to the Django static folder, so that it is served by Django.
|
||||
|
||||
### Development workflow with the frontend
|
||||
1. Edit frontend/src/ files
|
||||
2. Check changes on http://localhost:8088/
|
||||
3. Build frontend with `docker compose -f docker-compose-dev.yaml exec frontend npm run dist`
|
||||
4. Copy static files to Django static folder with`cp -r frontend/dist/static/* static/`
|
||||
5. Restart Django - `docker compose -f docker-compose-dev.yaml restart web` so that it uses the new static files
|
||||
6. Commit the changes
|
||||
|
||||
### Helper commands
|
||||
There is ongoing effort to provide helper commands, check the Makefile for what it supports. Eg
|
||||
|
||||
Bash into the web container:
|
||||
|
||||
```
|
||||
user@user:~/mediacms$ make admin-shell
|
||||
root@ca8c1096726b:/home/mediacms.io/mediacms# ./manage.py shell
|
||||
```
|
||||
|
||||
Build the frontend:
|
||||
|
||||
```
|
||||
user@user:~/mediacms$ make build-frontend
|
||||
docker compose -f docker-compose-dev.yaml exec frontend npm run dist
|
||||
|
||||
> mediacms-frontend@0.9.1 dist /home/mediacms.io/mediacms/frontend
|
||||
> mediacms-scripts rimraf ./dist && mediacms-scripts build --config=./config/mediacms.config.js --env=dist
|
||||
...
|
||||
```
|
||||
155
docs/developers_docs.md
Normal file
155
docs/developers_docs.md
Normal file
@@ -0,0 +1,155 @@
|
||||
# Developers documentation
|
||||
|
||||
## Table of contents
|
||||
- [1. Welcome](#1-welcome)
|
||||
- [2. System architecture](#2-system-architecture)
|
||||
- [3. API documentation](#3-api-documentation)
|
||||
- [4. How to contribute](#4-how-to-contribute)
|
||||
- [5. Working with Docker tips](#5-working-with-docker-tips)
|
||||
- [6. Working with the automated tests](#6-working-with-the-automated-tests)
|
||||
- [7. How video is transcoded](#7-how-video-is-transcoded)
|
||||
|
||||
## 1. Welcome
|
||||
This page is created for MediaCMS developers and contains related information.
|
||||
|
||||
## 2. System architecture
|
||||
to be written
|
||||
|
||||
## 3. API documentation
|
||||
API is documented using Swagger - checkout ot http://your_installation/swagger - example https://demo.mediacms.io/swagger/
|
||||
This page allows you to login to perform authenticated actions - it will also use your session if logged in.
|
||||
|
||||
|
||||
An example of working with Python requests library:
|
||||
|
||||
```
|
||||
import requests
|
||||
|
||||
auth = ('user' ,'password')
|
||||
upload_url = "https://domain/api/v1/media"
|
||||
title = 'x title'
|
||||
description = 'x description'
|
||||
media_file = '/tmp/file.mp4'
|
||||
|
||||
requests.post(
|
||||
url=upload_url,
|
||||
files={'media_file': open(media_file,'rb')},
|
||||
data={'title': title, 'description': description},
|
||||
auth=auth
|
||||
)
|
||||
```
|
||||
|
||||
## 4. How to contribute
|
||||
Before you send a PR, make sure your code is properly formatted. For that, use `pre-commit install` to install a pre-commit hook and run `pre-commit run --all` and fix everything before you commit. This pre-commit will check for your code lint everytime you commit a code.
|
||||
|
||||
Checkout the [Code of conduct page](../CODE_OF_CONDUCT.md) if you want to contribute to this repository
|
||||
|
||||
|
||||
## 5. Working with Docker tips
|
||||
|
||||
To perform the Docker installation, follow instructions to install Docker + Docker compose (docs/Docker_Compose.md) and then build/start docker-compose-dev.yaml . This will run the frontend application on port 8088 on top of all other containers (including the Django web application on port 80)
|
||||
|
||||
```
|
||||
docker compose -f docker-compose-dev.yaml build
|
||||
docker compose -f docker-compose-dev.yaml up
|
||||
```
|
||||
|
||||
An `admin` user is created during the installation process. Its attributes are defined in `docker-compose-dev.yaml`:
|
||||
```
|
||||
ADMIN_USER: 'admin'
|
||||
ADMIN_PASSWORD: 'admin'
|
||||
ADMIN_EMAIL: 'admin@localhost'
|
||||
```
|
||||
|
||||
### Frontend application changes
|
||||
Eg change `frontend/src/static/js/pages/HomePage.tsx` , dev application refreshes in a number of seconds (hot reloading) and I see the changes, once I'm happy I can run
|
||||
|
||||
```
|
||||
docker compose -f docker-compose-dev.yaml exec -T frontend npm run dist
|
||||
```
|
||||
|
||||
And then in order for the changes to be visible on the application while served through nginx,
|
||||
|
||||
```
|
||||
cp -r frontend/dist/static/* static/
|
||||
```
|
||||
|
||||
POST calls: cannot be performed through the dev server, you have to make through the normal application (port 80) and then see changes on the dev application on port 8088.
|
||||
Make sure the urls are set on `frontend/.env` if different than localhost
|
||||
|
||||
|
||||
Media page: need to upload content through the main application (nginx/port 80), and then use an id for page media.html, for example `http://localhost:8088/media.html?m=nc9rotyWP`
|
||||
|
||||
There are some issues with CORS too to resolve, in order for some pages to function, eg the manage comments page
|
||||
|
||||
```
|
||||
http://localhost:8088/manage-media.html manage_media
|
||||
```
|
||||
|
||||
### Backend application changes
|
||||
After I make changes to the django application (eg make a change on `files/forms.py`) in order to see the changes I have to restart the web container
|
||||
|
||||
```
|
||||
docker compose -f docker-compose-dev.yaml restart web
|
||||
```
|
||||
|
||||
## How video is transcoded
|
||||
|
||||
Original files get uploaded to the application server, and they get stored there as FileFields.
|
||||
|
||||
If files are videos and the duration is greater than a number (defined on settings, I think 4minutes), they are also broken in chunks, so one Encode object per chunk, for all enabled EncodeProfiles.
|
||||
|
||||
Then the workers start picking Encode objects and they transcode the chunks, so if a chunk gets transcoded correctly, the original file (the small chunk) gets replaced by the transcoded file, and the Encode object status is marked as 'success'.
|
||||
|
||||
|
||||
original.mp4 (1G, 720px)--> Encode1 (100MB, 240px, chunk=True), Encode2 (100MB, 240px, chunk=True)...EncodeXX (100MB, 720px, chunk=True) ---> when all Encode objects are success, for a resolution, they get concatenated to the original_resolution.mp4 file and this gets stored as Encode object (chunk=False). This is what is available for download.
|
||||
|
||||
Apparently the Encode object is used to store Encoded files that are served eventually (chunk=False, status='success'), but also files while they are on their way to get transcoded (chunk=True, status='pending/etc')
|
||||
|
||||
(Parenthesis opening)
|
||||
there is also an experimental small service (not commited to the repo currently) that speaks only through API and a) gets tasks to run, b) returns results. So it makes a request and receives an ffmpeg command, plus a file, it runs the ffmpeg command, and returns the result.I've used this mechanism on a number of installations to migrate existing videos through more servers/cpu and has worked with only one problem, some temporary files needed to be removed from the servers (through a periodic task, not so big problem)
|
||||
(Parenthesis closing)
|
||||
|
||||
|
||||
When the Encode object is marked as success and chunk=False, and thus is available for download/stream, there is a task that gets started and saves an HLS version of the file (1 mp4-->x number of small .ts chunks). This would be FILES_C
|
||||
|
||||
This mechanism allows for workers that have access on the same filesystem (either localhost, or through a shared network filesystem, eg NFS/EFS) to work on the same time and produce results.
|
||||
|
||||
## 6. Working with the automated tests
|
||||
|
||||
This instructions assume that you're using the docker installation
|
||||
|
||||
1. start docker-compose
|
||||
|
||||
```
|
||||
docker compose up
|
||||
```
|
||||
|
||||
2. Install the requirements on `requirements-dev.txt ` on web container (we'll use the web container for this)
|
||||
|
||||
```
|
||||
docker compose exec -T web pip install -r requirements-dev.txt
|
||||
```
|
||||
|
||||
3. Now you can run the existing tests
|
||||
|
||||
```
|
||||
docker compose exec --env TESTING=True -T web pytest
|
||||
```
|
||||
|
||||
The `TESTING=True` is passed for Django to be aware this is a testing environment (so that it runs Celery tasks as functions for example and not as background tasks, since Celery is not started in the case of pytest)
|
||||
|
||||
|
||||
4. You may try a single test, by specifying the path, for example
|
||||
|
||||
```
|
||||
docker compose exec --env TESTING=True -T web pytest tests/test_fixtures.py
|
||||
```
|
||||
|
||||
5. You can also see the coverage
|
||||
|
||||
```
|
||||
docker compose exec --env TESTING=True -T web pytest --cov=. --cov-report=html
|
||||
```
|
||||
|
||||
and of course...you are very welcome to help us increase it ;)
|
||||
BIN
docs/images/CC-display.png
Executable file
BIN
docs/images/CC-display.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 567 KiB |
BIN
docs/images/Click-ADD-button.png
Executable file
BIN
docs/images/Click-ADD-button.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/images/Click-Browse-button.png
Executable file
BIN
docs/images/Click-Browse-button.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/images/Click-Download-Button.png
Executable file
BIN
docs/images/Click-Download-Button.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 9.9 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user