Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/johnalbertini14-glitch/openclaw-skills --skill ephemeral-media-hosting명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | ephemeral-media-hosting |
| version | 1.0.0 |
| description | 自動削除機能付き一時メディアホスティングシステム |
チャット共有向けの自動削除機能付きメディアディレクトリシステム。7日間の自動保持、MIME検証、fetch-image.shパターン、nginx設定の包括的ガイドです。
/var/www/media/
├── temp/ # 一時ファイル(7日保持)
├── uploads/ # アップロード受付
├── processed/ # 処理済みファイル
├── logs/ # アクセス・処理ログ
└── scripts/ # 管理スクリプト群
#!/bin/bash
# media-setup.sh
setup_media_hosting() {
local media_root="/var/www/media"
local nginx_user="www-data"
echo "=== メディアホスティング初期設定 ==="
# ディレクトリ作成
sudo mkdir -p "$media_root"/{temp,uploads,processed,logs,scripts}
# 権限設定
sudo chown -R "$nginx_user:$nginx_user" "$media_root"
sudo chmod -R 755 "$media_root"
sudo chmod 775 "$media_root"/{uploads,temp,processed}
# 設定ファイル作成
cat > "$media_root/config.env" << 'EOF'
# メディアホスティング設定
MAX_FILE_SIZE=10M
RETENTION_DAYS=7
ALLOWED_MIMES="image/jpeg,image/png,image/gif,image/webp,video/mp4,video/webm"
UPLOAD_RATE_LIMIT=100
EOF
echo "初期設定完了: $media_root"
}
# 実行
setup_media_hosting
# /etc/nginx/sites-available/ephemeral-media
server {
listen 80;
server_name media.yourdomain.com;
# セキュリティヘッダー
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "DENY";
add_header X-XSS-Protection "1; mode=block";
add_header Content-Security-Policy "default-src 'none'; img-src 'self'; media-src 'self';";
# ファイルサイズ制限
client_max_body_size 10M;
# メディアルート
root /var/www/media;
index index.html;
# 一時ファイル配信
location /temp/ {
alias /var/www/media/temp/;
# キャッシュヘッダー(短期間)
expires 1h;
add_header Cache-Control "public, must-revalidate";
# セキュリティ
add_header X-Ephemeral "true";
add_header X-Expires-After "7-days";
# ファイル存在確認
try_files $uri @not_found;
# ログ記録
access_log /var/log/nginx/media-access.log combined;
}
# アップロード処理
location /upload {
# POST のみ許可
limit_except POST { deny all; }
# レート制限
limit_req zone=upload_zone burst=5 nodelay;
# PHP-FPM等へプロキシ
proxy_pass http://127.0.0.1:8080/upload;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# アップロードログ
access_log /var/log/nginx/upload-access.log upload_format;
}
# 直接ファイル配信
location ~* \.(jpg|jpeg|png|gif|webp|mp4|webm)$ {
# セキュリティチェック
if ($request_uri ~* "\.\./") {
return 403;
}
# MIME設定
location ~* \.jpg$ { add_header Content-Type "image/jpeg"; }
location ~* \.png$ { add_header Content-Type "image/png"; }
location ~* \.gif$ { add_header Content-Type "image/gif"; }
location ~* \.webp$ { add_header Content-Type "image/webp"; }
location ~* \.mp4$ { add_header Content-Type "video/mp4"; }
location ~* \.webm$ { add_header Content-Type "video/webm"; }
# レスポンシブ画像対応
expires 1d;
add_header Vary "Accept-Encoding";
}
# エラーページ
location @not_found {
return 404 "ファイルが見つからないか、有効期限が切れています";
}
# メインページ(アップロードフォーム等)
location = / {
try_files /index.html @upload_form;
}
location @upload_form {
return 200 '<!DOCTYPE html>
<html><head><title>一時メディア共有</title></head>
<body>
<h1>一時ファイル共有</h1>
<p>7日間で自動削除されます。</p>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="media" accept="image/*,video/*" required>
<button type="submit">アップロード</button>
</form>
</body></html>';
add_header Content-Type "text/html; charset=utf-8";
}
}
# レート制限設定
http {
limit_req_zone $binary_remote_addr zone=upload_zone:10m rate=10r/m;
# カスタムログフォーマット
log_format upload_format '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'upload_size:$content_length';
}
#!/bin/bash
# cleanup-ephemeral-media.sh
cleanup_old_files() {
local media_root="/var/www/media"
local retention_days="7"
local log_file="$media_root/logs/cleanup.log"
echo "$(date): 一時ファイル削除開始" >> "$log_file"
# 7日前より古いファイルを削除
local deleted_count=0
find "$media_root/temp" -type f -mtime +"$retention_days" -print0 | \
while IFS= read -r -d '' file; do
local file_size="$(stat -c%s "$file")"
local file_name="$(basename "$file")"
if rm "$file" 2>/dev/null; then
echo "$(date): 削除 $file_name ($file_size bytes)" >> "$log_file"
((deleted_count++))
else
echo "$(date): 削除失敗 " >>
find - d -empty -delete 2>/dev/null
total_files=
total_size=
>>
find -name -mtime +30 -delete
}
() {
cron_entry=
(crontab -l 2>/dev/null; ) | -u | crontab -
}
cleanup_old_files
#!/bin/bash
# mime-validator.sh
validate_file_mime() {
local file_path="$1"
local allowed_mimes="$2"
# 実際のMIMEタイプ検出
local detected_mime="$(file --mime-type -b "$file_path")"
local file_extension="${file_path##*.}"
echo "=== MIME検証: $(basename "$file_path") ==="
echo "検出MIME: $detected_mime"
echo "拡張子: $file_extension"
# 許可リストチェック
if echo "$allowed_mimes" | grep -q "$detected_mime"; then
echo "✓ MIME許可済み"
else
echo "✗ MIME不許可"
return 1
fi
# 拡張子とMIMEの整合性チェック
case "$detected_mime" in
"image/jpeg")
[[ "$file_extension" =~ ^(jpg|jpeg)$ ]] || { ; 1; } ;;
)
[[ == ]] || { ; 1; } ;;
)
[[ == ]] || { ; 1; } ;;
)
[[ == ]] || { ; 1; } ;;
)
[[ == ]] || { ; 1; } ;;
)
[[ == ]] || { ; 1; } ;;
*)
; 1 ;;
file_size=
max_size=
[[ -gt ]];
1
0
}
() {
file_path=
-v clamscan >/dev/null;
clamscan --quiet ;
0
1
0
}
validate_file_mime
#!/bin/bash
# fetch-image.sh
fetch_and_cache_image() {
local url="$1"
local media_root="/var/www/media"
local temp_dir="$media_root/temp"
# URL検証
if ! echo "$url" | grep -qE '^https?://'; then
echo "エラー: 無効なURL"
return 1
fi
# URLハッシュ生成(ファイル名用)
local url_hash="$(echo -n "$url" | sha256sum | cut -d' ' -f1)"
local timestamp="$(date +%s)"
# ファイル名推測
local suggested_ext="jpg"
case "$url" in
*.png*) suggested_ext="png" ;;
*.gif*) suggested_ext="gif" ;;
*.webp*) suggested_ext="webp" ;;
*.jpeg*|*.jpg*) suggested_ext="jpg" ;;
esac
local output_file="$temp_dir/${timestamp}_${url_hash:0:12}.$suggested_ext"
echo
curl -L --max-time 30 --max-filesize 10485760 \
-H \
-o \
;
[[ -f ]] &&
1
! validate_file_mime ;
1
file_size=
public_url=
> <<
0
}
() {
input_file=
max_width=
quality=
! -v convert >/dev/null;
0
output_file=
convert \
-resize \
-quality \
-strip \
[[ -f ]];
}
() {
url_list_file=
IFS= -r url;
[[ -z || =~ ^# ]] &&
fetch_and_cache_image ;
1
<
}
<?php
// upload.php - シンプルなアップロード処理
header('Content-Type: application/json; charset=utf-8');
// エラーハンドリング
function sendError($message, $code = 400) {
http_response_code($code);
echo json_encode(['error' => $message]);
exit;
}
// 設定
$config = [
'upload_dir' => '/var/www/media/temp/',
'max_size' => 10 * 1024 * 1024, // 10MB
'allowed_types' => ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'video/mp4', 'video/webm'],
'retention_days' => 7
];
// POST確認
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
sendError('POST method required', 405);
}
// ファイルアップロード確認
if (!isset($_FILES['media']) || $_FILES['media'][] !== UPLOAD_ERR_OK) {
( . ([][] ?? ));
}
= [];
([] > []) {
( . ([] / / , ) . );
}
= (FILEINFO_MIME_TYPE);
= (, []);
();
(!(, [])) {
( . );
}
= [
=> ,
=> ,
=> ,
=> ,
=> ,
=>
];
= [];
= ();
= (());
= (, , , );
= [] . ;
(!([], )) {
();
}
(, );
= [
=> ,
=> [],
=> ,
=> [],
=> ,
=> + ([] * ),
=> [] ??
];
( . , (, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
= . ;
= (
,
(),
,
,
[],
[] ??
);
(, , FILE_APPEND | LOCK_EX);
([
=> ,
=> ,
=> ,
=> [],
=> ,
=> [] . ,
=> (, [])
]);
#!/bin/bash
# media-stats.sh
generate_media_stats() {
local media_root="/var/www/media"
local stats_file="$media_root/logs/daily-stats.json"
local date="$(date -I)"
echo "=== メディア統計生成: $date ==="
# ファイル数・サイズ統計
local temp_files="$(find "$media_root/temp" -type f -name "*.jpg" -o -name "*.png" -o -name "*.gif" -o -name "*.webp" -o -name "*.mp4" -o -name "*.webm" | wc -l)"
local temp_size="$(du -sb "$media_root/temp" 2>/dev/null | cut -f1)"
# 今日のアップロード数
local today_uploads="$(grep "$(date '+%Y-%m-%d')" "$media_root/logs/uploads.log" 2>/dev/null | wc -l)"
# アクセス統計
local today_requests="$(grep "$(date '+%d/%b/%Y')" /var/log/nginx/media-access.log 2>/dev/null | wc -l)"
local unique_ips="$(grep "$(date '+%d/%b/%Y')" /var/log/nginx/media-access.log 2>/dev/null | awk '{print $1}' | sort -u | wc -l)"
> <<
}
() {
media_root=
temp_size=
max_size=
[[ -gt ]];
find - f -mtime +3 -delete
find - f \( -name -o -name \) | file;
! file | grep -q ;
}
generate_media_stats
check_alerts
コンテンツ審査と高度な無害化については太郎書館を参照。