Add Gitea Actions deploy workflow and server configuration
Some checks failed
Deploy to Server / deploy (push) Failing after 1m49s
Some checks failed
Deploy to Server / deploy (push) Failing after 1m49s
- Add basePath /admin to Next.js config for path-based routing - Add .gitea/workflows/deploy.yml for CI/CD via Gitea Actions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -570,9 +570,7 @@ pub async fn list_entries(
|
||||
Some(&*registry),
|
||||
)
|
||||
.await;
|
||||
if resolve.is_some() {
|
||||
expand_asset_urls(item, &state.base_url);
|
||||
}
|
||||
expand_asset_urls(item, &state.base_url);
|
||||
}
|
||||
|
||||
let response_value = serde_json::to_value(&result).unwrap();
|
||||
@@ -676,9 +674,7 @@ pub async fn get_entry(
|
||||
Some(&*registry),
|
||||
)
|
||||
.await;
|
||||
if resolve.is_some() {
|
||||
expand_asset_urls(&mut formatted, &state.base_url);
|
||||
}
|
||||
expand_asset_urls(&mut formatted, &state.base_url);
|
||||
|
||||
// Only cache published entries so unauthenticated requests never see cached drafts.
|
||||
if !entry_is_draft(&formatted) {
|
||||
@@ -844,7 +840,7 @@ pub async fn create_entry(
|
||||
.await
|
||||
.map_err(ApiError::from)?
|
||||
.unwrap();
|
||||
let formatted = format_references(
|
||||
let mut formatted = format_references(
|
||||
entry,
|
||||
&schema,
|
||||
store.as_ref(),
|
||||
@@ -853,6 +849,7 @@ pub async fn create_entry(
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
expand_asset_urls(&mut formatted, &state.base_url);
|
||||
|
||||
webhooks::fire(
|
||||
state.http_client.clone(),
|
||||
@@ -1003,7 +1000,7 @@ pub async fn update_entry(
|
||||
.await
|
||||
.map_err(ApiError::from)?
|
||||
.unwrap();
|
||||
let formatted = format_references(
|
||||
let mut formatted = format_references(
|
||||
entry,
|
||||
&schema,
|
||||
store.as_ref(),
|
||||
@@ -1012,6 +1009,7 @@ pub async fn update_entry(
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
expand_asset_urls(&mut formatted, &state.base_url);
|
||||
|
||||
webhooks::fire(
|
||||
state.http_client.clone(),
|
||||
|
||||
@@ -9,11 +9,19 @@ use crate::schema::SchemaRegistry;
|
||||
use crate::store::ContentStore;
|
||||
|
||||
/// Recursively expand relative `/api/assets/` paths to absolute URLs.
|
||||
/// Idempotent: strings already starting with a scheme (http/https) are skipped.
|
||||
/// Whole-string values and occurrences inside strings (e.g. markdown) are expanded.
|
||||
/// Idempotent: already-expanded base URL is preserved via placeholder during replace.
|
||||
pub fn expand_asset_urls(value: &mut Value, base_url: &str) {
|
||||
let base = base_url.trim_end_matches('/');
|
||||
let full_prefix = format!("{}/api/assets/", base);
|
||||
match value {
|
||||
Value::String(s) if s.starts_with("/api/assets/") => {
|
||||
*s = format!("{}{}", base_url.trim_end_matches('/'), s);
|
||||
Value::String(s) if s.contains("/api/assets/") => {
|
||||
// Avoid double-expand: temporarily replace already-expanded URLs
|
||||
const PLACEHOLDER: &str = "\x00__ASSET_BASE__\x00";
|
||||
*s = s
|
||||
.replace(&full_prefix, PLACEHOLDER)
|
||||
.replace("/api/assets/", &full_prefix)
|
||||
.replace(PLACEHOLDER, &full_prefix);
|
||||
}
|
||||
Value::Array(arr) => arr.iter_mut().for_each(|v| expand_asset_urls(v, base_url)),
|
||||
Value::Object(map) => map.values_mut().for_each(|v| expand_asset_urls(v, base_url)),
|
||||
@@ -23,11 +31,12 @@ pub fn expand_asset_urls(value: &mut Value, base_url: &str) {
|
||||
|
||||
/// Reverse of expand_asset_urls: strip the base_url prefix from absolute asset URLs
|
||||
/// before persisting to disk, so stored paths are always relative.
|
||||
/// Whole-string values and occurrences inside strings (e.g. markdown) are collapsed.
|
||||
pub fn collapse_asset_urls(value: &mut Value, base_url: &str) {
|
||||
let prefix = format!("{}/api/assets/", base_url.trim_end_matches('/'));
|
||||
match value {
|
||||
Value::String(s) if s.starts_with(&prefix) => {
|
||||
*s = format!("/api/assets/{}", &s[prefix.len()..]);
|
||||
Value::String(s) if s.contains(&prefix) => {
|
||||
*s = s.replace(&prefix, "/api/assets/");
|
||||
}
|
||||
Value::Array(arr) => arr.iter_mut().for_each(|v| collapse_asset_urls(v, base_url)),
|
||||
Value::Object(map) => map.values_mut().for_each(|v| collapse_asset_urls(v, base_url)),
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde_json::Value;
|
||||
use tokio::fs;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
@@ -34,6 +35,32 @@ impl FileStore {
|
||||
.join(format!("{}.json5", slug))
|
||||
}
|
||||
|
||||
/// Attach _created and _updated (ISO8601) from file metadata when available.
|
||||
async fn attach_file_timestamps(&self, path: &Path, value: &mut Value) {
|
||||
let meta = match fs::metadata(path).await {
|
||||
Ok(m) => m,
|
||||
Err(_) => return,
|
||||
};
|
||||
let modified = match meta.modified() {
|
||||
Ok(t) => t,
|
||||
Err(_) => return,
|
||||
};
|
||||
let modified_dt: DateTime<Utc> = modified.into();
|
||||
let updated = modified_dt.to_rfc3339();
|
||||
let created = meta
|
||||
.created()
|
||||
.ok()
|
||||
.map(|t| {
|
||||
let dt: DateTime<Utc> = t.into();
|
||||
dt.to_rfc3339()
|
||||
})
|
||||
.unwrap_or_else(|| updated.clone());
|
||||
if let Some(obj) = value.as_object_mut() {
|
||||
obj.insert("_created".to_string(), Value::String(created));
|
||||
obj.insert("_updated".to_string(), Value::String(updated));
|
||||
}
|
||||
}
|
||||
|
||||
/// Path for optional external content file: same dir as entry, stem.content.md.
|
||||
/// Used so markdown (and other long text) can live in a .md file instead of JSON.
|
||||
fn content_file_path(&self, entry_path: &Path) -> PathBuf {
|
||||
@@ -178,6 +205,7 @@ impl FileStore {
|
||||
match self.read_file(&path).await {
|
||||
Ok(mut value) => {
|
||||
let _ = self.resolve_content_file(&path, &mut value).await;
|
||||
self.attach_file_timestamps(&path, &mut value).await;
|
||||
if let Some(obj) = value.as_object_mut() {
|
||||
obj.insert("_slug".to_string(), Value::String(slug.clone()));
|
||||
}
|
||||
@@ -201,6 +229,7 @@ impl FileStore {
|
||||
|
||||
let mut value = self.read_file(&path).await?;
|
||||
self.resolve_content_file(&path, &mut value).await?;
|
||||
self.attach_file_timestamps(&path, &mut value).await;
|
||||
if let Some(obj) = value.as_object_mut() {
|
||||
obj.insert("_slug".to_string(), Value::String(slug.to_string()));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user