diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..d232c8b --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,29 @@ +name: Deploy to Server + +on: + push: + branches: + - main + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build API image + run: | + docker build -t rustycms-api:latest . + + - name: Build Admin UI image + run: | + docker build \ + --build-arg NEXT_PUBLIC_RUSTYCMS_API_URL=https://cms.pm86.de \ + -t rustycms-admin:latest \ + ./admin-ui + + - name: Deploy + run: | + docker compose -f /opt/rustycms/docker-compose.yml up -d + docker image prune -f diff --git a/admin-ui/messages/de.json b/admin-ui/messages/de.json index 621f930..04e632d 100644 --- a/admin-ui/messages/de.json +++ b/admin-ui/messages/de.json @@ -32,6 +32,7 @@ "slugHint": "Kleinbuchstaben (a-z), Ziffern (0-9), Bindestriche. Leerzeichen werden zu Bindestrichen.", "savedSuccessfully": "Erfolgreich gespeichert.", "errorSaving": "Fehler beim Speichern", + "validationErrors": "Bitte korrigieren Sie die Fehler im Formular (z. B. Pflichtfelder).", "saving": "Speichern…", "save": "Speichern", "backToList": "Zurück zur Liste", @@ -175,7 +176,11 @@ "filterByTag": "Tag:", "tagAll": "Alle", "noResults": "Kein Inhaltstyp entspricht Suche oder Filter.", - "noCollections": "Keine Sammlungen geladen. Pr\u00fcfe ob die RustyCMS-API unter {url} erreichbar ist." + "noCollections": "Keine Sammlungen geladen. Pr\u00fcfe ob die RustyCMS-API unter {url} erreichbar ist.", + "recentSectionTitle": "Letzte 3 bearbeitete Beitr\u00e4ge", + "recentSectionLink": "Alle Beitr\u00e4ge", + "recentSectionEmpty": "Noch keine Beitr\u00e4ge.", + "loading": "Laden…" }, "TypesPage": { "title": "Typen", @@ -355,6 +360,8 @@ "newEntry": "Neuer Eintrag", "colActions": "Aktionen", "colStatus": "Status", + "colCreated": "Erstellt", + "colLastEdited": "Zuletzt geändert", "published": "Veröffentlicht", "noEntries": "Keine Einträge.", "noEntriesCreate": "Noch keine Einträge. Erstellen Sie den ersten.", @@ -382,6 +389,8 @@ }, "ContentEditPage": { "title": "Eintrag bearbeiten", + "created": "Erstellt", + "lastEdited": "Zuletzt geändert", "apiLink": "API-Link (Daten-Vorschau):", "referrersSection": "Referenziert von", "noReferrers": "Kein anderer Eintrag verweist auf diesen.", diff --git a/admin-ui/messages/en.json b/admin-ui/messages/en.json index c897232..204dde7 100644 --- a/admin-ui/messages/en.json +++ b/admin-ui/messages/en.json @@ -32,6 +32,7 @@ "slugHint": "Lowercase letters (a-z), digits (0-9), hyphens. Spaces become hyphens.", "savedSuccessfully": "Saved successfully.", "errorSaving": "Error saving", + "validationErrors": "Please fix the errors in the form (e.g. required fields).", "saving": "Saving…", "save": "Save", "backToList": "Back to list", @@ -175,7 +176,11 @@ "filterByTag": "Tag:", "tagAll": "All", "noResults": "No content types match your search or filter.", - "noCollections": "No collections loaded. Check that the RustyCMS API is running at {url}." + "noCollections": "No collections loaded. Check that the RustyCMS API is running at {url}.", + "recentSectionTitle": "Last 3 edited posts", + "recentSectionLink": "All posts", + "recentSectionEmpty": "No posts yet.", + "loading": "Loading…" }, "TypesPage": { "title": "Types", @@ -355,6 +360,8 @@ "newEntry": "New entry", "colActions": "Actions", "colStatus": "Status", + "colCreated": "Created", + "colLastEdited": "Last edited", "published": "Published", "noEntries": "No entries.", "noEntriesCreate": "No entries yet. Create the first one.", @@ -382,6 +389,8 @@ }, "ContentEditPage": { "title": "Edit entry", + "created": "Created", + "lastEdited": "Last edited", "apiLink": "API link (data preview):", "referrersSection": "Referenced by", "noReferrers": "No other entries reference this one.", diff --git a/admin-ui/next.config.ts b/admin-ui/next.config.ts index 7e5afee..cd2689e 100644 --- a/admin-ui/next.config.ts +++ b/admin-ui/next.config.ts @@ -4,4 +4,5 @@ const withNextIntl = createNextIntlPlugin('./i18n/request.ts'); export default withNextIntl({ output: 'standalone', + basePath: '/admin', }); diff --git a/admin-ui/package-lock.json b/admin-ui/package-lock.json index c0d0259..2f73b76 100644 --- a/admin-ui/package-lock.json +++ b/admin-ui/package-lock.json @@ -24,6 +24,7 @@ "react-hook-form": "^7.71.1", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^16.1.1", + "rehype-raw": "^7.0.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tailwindcss-animate": "^1.0.7" @@ -6316,6 +6317,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", @@ -7415,6 +7428,26 @@ "node": ">= 0.4" } }, + "node_modules/hast-util-from-parse5": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", + "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "devlop": "^1.0.0", + "hastscript": "^9.0.0", + "property-information": "^7.0.0", + "vfile": "^6.0.0", + "vfile-location": "^5.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-parse-selector": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", @@ -7428,6 +7461,31 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-raw": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", + "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "@ungap/structured-clone": "^1.0.0", + "hast-util-from-parse5": "^8.0.0", + "hast-util-to-parse5": "^8.0.0", + "html-void-elements": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "parse5": "^7.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-to-jsx-runtime": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", @@ -7455,6 +7513,25 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/hast-util-to-parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.1.tgz", + "integrity": "sha512-MlWT6Pjt4CG9lFCjiz4BH7l9wmrMkfkJYCxFwKQic8+RTZgWPuWxwAfjJElsXkex7DJjfSJsQIt931ilUgmwdA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "web-namespaces": "^2.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/hast-util-whitespace": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", @@ -7527,6 +7604,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/html-void-elements": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", + "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/icu-minify": { "version": "4.8.3", "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.8.3.tgz", @@ -9718,6 +9805,18 @@ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", "license": "MIT" }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -10201,6 +10300,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/rehype-raw": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", + "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "hast-util-raw": "^9.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", @@ -11424,6 +11538,20 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/vfile-location": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", + "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/vfile-message": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", @@ -11438,6 +11566,16 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/web-namespaces": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", + "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/admin-ui/package.json b/admin-ui/package.json index 921aac2..e047f3b 100644 --- a/admin-ui/package.json +++ b/admin-ui/package.json @@ -25,6 +25,7 @@ "react-hook-form": "^7.71.1", "react-markdown": "^10.1.0", "react-syntax-highlighter": "^16.1.1", + "rehype-raw": "^7.0.0", "sonner": "^2.0.7", "tailwind-merge": "^3.5.0", "tailwindcss-animate": "^1.0.7" diff --git a/admin-ui/src/app/content/[collection]/[slug]/page.tsx b/admin-ui/src/app/content/[collection]/[slug]/page.tsx index af56819..852c106 100644 --- a/admin-ui/src/app/content/[collection]/[slug]/page.tsx +++ b/admin-ui/src/app/content/[collection]/[slug]/page.tsx @@ -204,9 +204,25 @@ export default function ContentEditPage() { )} -
+ {entry._created != null && ( + + {t("created")}: {new Date(String(entry._created)).toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" })} + + )} + {entry._created != null && entry._updated != null && " · "} + {entry._updated != null && ( + + {t("lastEdited")}: {new Date(String(entry._updated)).toLocaleString(undefined, { dateStyle: "short", timeStyle: "short" })} + + )} +
+ )} + {entry && !entry._created && !entry._updated && } {isLoading && (