Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
1050334
Add stats.html to .gitignore
johan-bell Oct 18, 2025
2dbe9f8
APP: Add local development S3 settings for media uploads
johan-bell Oct 18, 2025
f745d46
APP: Add media-related S3 environment variables to deployment workflow
johan-bell Oct 18, 2025
0a4fe15
APP: Add S3MediaConfig type and integrate media settings into configu…
johan-bell Oct 18, 2025
e1b117e
APP: Implement S3MediaService for media management with Minio integra…
johan-bell Oct 18, 2025
ffbe897
APP: Enhance testing module to include S3MediaService and configurati…
johan-bell Oct 18, 2025
a715c21
APP: Add music-metadata package for audio file metadata extraction
johan-bell Oct 18, 2025
286a80a
APP: Fix import path for S3MediaService in testing module
johan-bell Oct 18, 2025
e33b389
APP: Import S3MediaService and add it to providers in AppModule
johan-bell Oct 18, 2025
5f29c09
APP: Add unit tests for S3MediaService functionality
johan-bell Oct 18, 2025
58df086
APP: Add MediaDto, MediaFileDto, and MediaUploadDataDto for media han…
johan-bell Oct 18, 2025
682cd65
Add IsAudio decorator for audio file validation
johan-bell Oct 18, 2025
b046d9a
API: Implement robust audio format detection using music-metadata lib…
johan-bell Oct 18, 2025
9aca285
APP: Add unit tests for audio format detection functionality
johan-bell Oct 18, 2025
234f82b
Add MediaType and MediaPreset enums for media handling
johan-bell Oct 18, 2025
69e7b10
Add silence.wav audio file for testing purposes
johan-bell Oct 18, 2025
9f44b3e
Add media handling functions for processing and validating uploads
johan-bell Oct 18, 2025
820102a
Add unit tests for S3MediaHandler to validate media processing and ma…
johan-bell Oct 18, 2025
fba785a
Add optional maxMediaUploadFileSize to SocketIoConfig for media uploads
johan-bell Oct 18, 2025
45614e9
Add maxMediaUploadFileSize to ClientConfig and update clientConfigReq…
johan-bell Oct 18, 2025
6bc3c15
Add optional media field to _contentParentDto for media handling
johan-bell Oct 18, 2025
57e9d8e
Add optional parentMedia field to ContentDto for media handling
johan-bell Oct 18, 2025
2aee31a
Add S3MediaService to processChangeRequest for media handling
johan-bell Oct 18, 2025
85924d6
Add MediaDto to DocTypeMap for change request validation
johan-bell Oct 18, 2025
2c2ce74
Add media processing to processPostTagDto for improved media handling
johan-bell Oct 18, 2025
386800b
Add parentMedia field to ContentDto for improved media handling
johan-bell Oct 18, 2025
29f84ca
Refactor handleChangeRequest to improve file upload handling and util…
johan-bell Oct 18, 2025
3d427ae
Add createUploadData function for structured file upload handling
johan-bell Oct 18, 2025
1541751
Add S3MediaService injection to ChangeRequestService for enhanced med…
johan-bell Oct 18, 2025
429ab27
Update ChangeRequestService instantiation to include additional param…
johan-bell Oct 18, 2025
1345c1d
Inject S3MediaService into processChangeRequest tests for enhanced me…
johan-bell Oct 18, 2025
6e4759a
Inject S3MediaService into processChangeRequest tests for enhanced me…
johan-bell Oct 18, 2025
a8c52db
Enhance processChangeRequest to support media handling with S3MediaSe…
johan-bell Oct 18, 2025
0a1b98b
Add MediaType and MediaPreset enums for enhanced media handling
johan-bell Oct 18, 2025
32c96e0
Add MediaDto and related types for enhanced media handling
johan-bell Oct 18, 2025
712aa83
Add maxMediaUploadFileSize to ClientConfig and update socket handling
johan-bell Oct 18, 2025
edaf4bf
Add maxMediaUploadFileSize to socketio tests for enhanced media handling
johan-bell Oct 18, 2025
cedc380
Enhance upgradeDbSchema and v3 schema upgrade to include S3MediaServi…
johan-bell Oct 18, 2025
21b3c81
Add S3MediaService to bootstrap for enhanced database schema upgrades
johan-bell Oct 18, 2025
830609a
Refactor cleanup logic in S3MediaHandler tests for improved readabili…
johan-bell Oct 18, 2025
f839f1d
Add S3 Media storage access details to the test environment configura…
johan-bell Oct 18, 2025
b97f98a
Refactor cleanup logic in S3MediaHandler tests for improved error han…
johan-bell Oct 18, 2025
1ae43e5
Update cleanup comment in S3MediaHandler tests for clarity
johan-bell Oct 18, 2025
1ed0615
Enhance LBadge component props for customizable padding and rounded c…
johan-bell Oct 18, 2025
7ffbcfd
Add optional media property to ContentParentDto type
johan-bell Oct 19, 2025
847f65d
Remove MediaDto from DocTypeMap in validateChangeRequest
johan-bell Oct 19, 2025
b9bf7ce
Add MediaEditorThumbnail component for audio file playback and manage…
johan-bell Oct 19, 2025
9bf6a94
Add MediaEditor component for audio file upload and management
johan-bell Oct 19, 2025
691368a
Add unit tests for MediaEditor component functionality and file uploa…
johan-bell Oct 19, 2025
bf315a2
Add EditContentMedia component for audio file upload and management
johan-bell Oct 19, 2025
9a0ce29
Add unit tests for EditContentMedia component to verify audio thumbna…
johan-bell Oct 19, 2025
da7ae85
Integrate EditContentMedia component for enhanced media management in…
johan-bell Oct 19, 2025
e192c8d
Add mock media data for audio files in mockdata.ts
johan-bell Oct 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/api-deploy-staging.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ jobs:
echo "S3_ACCESS_KEY='$( echo '${{ secrets.S3_ACCESS_KEY }}' )'" >> .env
echo "S3_SECRET_KEY='$( echo '${{ secrets.S3_SECRET_KEY }}' )'" >> .env
echo "S3_IMG_BUCKET='$( echo '${{ secrets.S3_IMG_BUCKET }}' )'" >> .env
echo "S3_MEDIA_BUCKET='$( echo '${{ secrets.S3_MEDIA_BUCKET }}' )'" >> .env
echo "S3_MEDIA_PORT='$( echo '${{ secrets.S3_MEDIA_PORT }}' )'" >> .env
echo "S3_MEDIA_USE_SSL='$( echo '${{ secrets.S3_MEDIA_USE_SSL }}' )'" >> .env
echo "S3_MEDIA_ACCESS_KEY='$( echo '${{ secrets.S3_MEDIA_ACCESS_KEY }}' )'" >> .env
echo "S3_MEDIA_SECRET_KEY='$( echo '${{ secrets.S3_MEDIA_SECRET_KEY }}' )'" >> .env
echo "S3_MEDIA_ENDPOINT='$( echo '${{ secrets.S3_MEDIA_ENDPOINT }}' )'" >> .env
echo "CORS_ORIGIN='$( echo '${{ vars.CORS_ORIGIN }}' )'" >> .env
echo "JWT_MAPPINGS='$( echo '${{ vars.JWT_MAPPINGS }}' )'" >> .env
echo "MAX_HTTP_BUFFER_SIZE='$( echo '${{ vars.MAX_HTTP_BUFFER_SIZE }}' )'" >> .env
Expand Down
11 changes: 11 additions & 0 deletions api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ S3_SECRET_KEY="minio123"
S3_IMG_BUCKET="images"
S3_IMG_QUALITY=80

# Local development S3 settings
S3_MEDIA_ENDPOINT="localhost"
S3_MEDIA_PORT=9000
S3_MEDIA_USE_SSL=false
S3_MEDIA_ACCESS_KEY="minio"
S3_MEDIA_SECRET_KEY="minio123"
S3_MEDIA_BUCKET="audios"

# Socket.io settings
# Optional maximum upload file size in bytes (default 10MB)
MAX_HTTP_BUFFER_SIZE=10000000

# Maximum file size for media uploads in bytes
MAX_MEDIA_UPLOAD_FILE_SIZE=15000000 # 15MB
7 changes: 7 additions & 0 deletions api/.env.test.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@ S3_PORT=9000
S3_USE_SSL=false
S3_ACCESS_KEY="minio"
S3_SECRET_KEY="minio123"

# S3 Media storage access details (defaults to same as S3 if not set)
S3_MEDIA_ENDPOINT="127.0.0.1"
S3_MEDIA_PORT=9000
S3_MEDIA_USE_SSL=false
S3_MEDIA_ACCESS_KEY="minio"
S3_MEDIA_SECRET_KEY="minio123"
91 changes: 91 additions & 0 deletions api/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"class-validator": "^0.14.1",
"jsonwebtoken": "^9.0.2",
"minio": "^8.0.0",
"music-metadata": "^11.9.0",
"nano": "^10.1.4",
"nest-winston": "^1.9.4",
"reflect-metadata": "^0.1.13",
Expand Down
10 changes: 9 additions & 1 deletion api/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { SearchService } from "./endpoints/search.service";
import { ChangeRequestService } from "./endpoints/changeRequest.service";
import { ChangeRequestController } from "./endpoints/changeRequest.controller";
import * as winston from "winston";
import { S3MediaService } from "./s3-media/media.service";

let winstonTransport: winston.transport;
if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
Expand Down Expand Up @@ -47,6 +48,13 @@ if (!process.env.NODE_ENV || process.env.NODE_ENV === "development") {
}),
],
controllers: [AppController, SearchController, ChangeRequestController],
providers: [DbService, Socketio, S3Service, SearchService, ChangeRequestService],
providers: [
DbService,
Socketio,
S3Service,
S3MediaService,
SearchService,
ChangeRequestService,
],
})
export class AppModule {}
78 changes: 66 additions & 12 deletions api/src/changeRequests/documentProcessing/processContentDto.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import { ChangeReqDto } from "../../dto/ChangeReqDto";
import { PostDto } from "../../dto/PostDto";
import { PublishStatus } from "../../enums";
import { TagDto } from "../../dto/TagDto";
import { S3MediaService } from "../../s3-media/media.service";

describe("processContentDto", () => {
let db: DbService;
let s3: S3Service;
let s3Media: S3MediaService;

beforeAll(async () => {
const testingModule = await createTestingModule("process-content-dto");
db = testingModule.dbService;
s3 = testingModule.s3Service;
s3Media = testingModule.s3MediaService;
PermissionSystem.upsertGroups((await db.getGroups()).docs);
});

Expand All @@ -27,7 +30,14 @@ describe("processContentDto", () => {
changeRequest.doc._id = "test-slug-1";
changeRequest.doc.slug = "this-is-a-test-slug";

const res = await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
const res = await processChangeRequest(
"",
changeRequest,
["group-super-admins"],
db,
s3,
s3Media,
);
const dbDoc = await db.getDoc(changeRequest.doc._id);

expect(res.result.ok).toBe(true);
Expand All @@ -40,8 +50,15 @@ describe("processContentDto", () => {
changeRequest.doc._id = "test-slug-1";
changeRequest.doc.slug = "this-is-a-test-slug";

await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3); // ensure that the slug is already in use
const res = await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3, s3Media); // ensure that the slug is already in use
const res = await processChangeRequest(
"",
changeRequest,
["group-super-admins"],
db,
s3,
s3Media,
);
const dbDoc = await db.getDoc(changeRequest.doc._id);

expect(res.result.ok).toBe(true);
Expand All @@ -54,15 +71,22 @@ describe("processContentDto", () => {
changeRequest1.doc.parentId = "post-blog1";
changeRequest1.doc._id = "test-slug-1";
changeRequest1.doc.slug = "this-is-a-test-slug";
await processChangeRequest("", changeRequest1, ["group-super-admins"], db, s3);
await processChangeRequest("", changeRequest1, ["group-super-admins"], db, s3, s3Media);

// Create a new change request with the same slug
const changeRequest2 = changeRequest_content();
changeRequest2.doc.parentId = "post-blog1";
changeRequest2.doc._id = "test-slug-2";
changeRequest2.doc.slug = "this-is-a-test-slug";

const res = await processChangeRequest("", changeRequest2, ["group-super-admins"], db, s3);
const res = await processChangeRequest(
"",
changeRequest2,
["group-super-admins"],
db,
s3,
s3Media,
);
const dbDoc = await db.getDoc(changeRequest2.doc._id);

expect(res.result.ok).toBe(true);
Expand All @@ -75,7 +99,14 @@ describe("processContentDto", () => {
changeRequest.doc._id = "test-slug-1";
changeRequest.doc.slug = 'Invalid Slug! 123 無效的 Bør ikke være tilladt "#$%&/()=?`';

const res = await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
const res = await processChangeRequest(
"",
changeRequest,
["group-super-admins"],
db,
s3,
s3Media,
);
const dbDoc = await db.getDoc(changeRequest.doc._id);

expect(res.result.ok).toBe(true);
Expand All @@ -91,7 +122,7 @@ describe("processContentDto", () => {
delete changeRequest.doc.parentPostType;
delete changeRequest.doc.parentPublishDateVisible;

await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3, s3Media);
const dbDoc = await db.getDoc(changeRequest.doc._id);

expect(dbDoc.docs[0].memberOf).toEqual(["group-public-content"]);
Expand Down Expand Up @@ -121,7 +152,7 @@ describe("processContentDto", () => {
delete changeRequest.doc.parentPublishDateVisible;
delete changeRequest.doc.parentTaggedDocs;

await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3, s3Media);
const dbDoc = await db.getDoc(changeRequest.doc._id);

expect(dbDoc.docs[0].memberOf).toEqual(["group-public-content"]);
Expand All @@ -147,7 +178,7 @@ describe("processContentDto", () => {
};

PermissionSystem.upsertGroups((await db.getGroups()).docs);
await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3);
await processChangeRequest("", changeRequest, ["group-super-admins"], db, s3, s3Media);

const res = await db.getContentByParentId(changeRequest.doc._id);
const docsCount = res.docs.length;
Expand Down Expand Up @@ -178,7 +209,14 @@ describe("processContentDto", () => {
changeRequest1.doc.parentId = "post-blog1";
changeRequest1.doc._id = "content-en";
changeRequest1.doc.language = "lang-eng";
await processChangeRequest("test-user", changeRequest1, ["group-super-admins"], db, s3);
await processChangeRequest(
"test-user",
changeRequest1,
["group-super-admins"],
db,
s3,
s3Media,
);

// Add a new translation for the same parent
const changeRequest2 = changeRequest_content();
Expand All @@ -191,6 +229,7 @@ describe("processContentDto", () => {
["group-super-admins"],
db,
s3,
s3Media,
);

// Fetch the documents from the database
Expand All @@ -214,7 +253,14 @@ describe("processContentDto", () => {
changeRequest1.doc._id = "content-en";
changeRequest1.doc.language = "lang-eng";
changeRequest1.doc.status = PublishStatus.Published;
await processChangeRequest("test-user", changeRequest1, ["group-super-admins"], db, s3);
await processChangeRequest(
"test-user",
changeRequest1,
["group-super-admins"],
db,
s3,
s3Media,
);

// Add a new translation for the same parent
const changeRequest2 = changeRequest_content();
Expand All @@ -228,6 +274,7 @@ describe("processContentDto", () => {
["group-super-admins"],
db,
s3,
s3Media,
);

// Fetch the documents from the database
Expand All @@ -245,7 +292,14 @@ describe("processContentDto", () => {

// Update the English content document to draft
changeRequest1.doc.status = PublishStatus.Draft;
await processChangeRequest("test-user", changeRequest1, ["group-super-admins"], db, s3);
await processChangeRequest(
"test-user",
changeRequest1,
["group-super-admins"],
db,
s3,
s3Media,
);

// Fetch the documents from the database
const dbDocEn2 = await db.getDoc("content-en");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export default async function processContentDto(doc: ContentDto, db: DbService)
doc.memberOf = parentDoc.memberOf;
doc.parentTags = parentDoc.tags;
doc.parentImageData = parentDoc.imageData;
doc.parentMedia = parentDoc.media;

if (parentDoc.type == DocType.Post) {
doc.parentPostType = (parentDoc as PostDto).postType;
Expand Down
Loading