陈仓媒体中转 · 全部源码

陈仓的文字消息不经过任何服务器。语音、图片、视频先在手机上加密,再以密文形式经这里中转;App 内 24 小时后显示「已过期」,云端按 S3 生命周期约 1–2 天内自动删除。服务器拿不到任何密钥,也不记录 IP。下面就是线上运行的全部代码。

运营者声明的线上部署包 SHA-256:56fc10b0b7cc81c439f08753062c5171d8215cd320ff730685037c8fc98b0c9c。可核对方法:下载 lambda/index.mjs 与 lambda/sign.mjs,按 deploy.sh 第 1 步打包后计算 SHA-256,与此值以及 curl -sI "https://d3nnqcgewrb4f0.cloudfront.net/api/upload" | grep x-cc-source-sha256 的结果比对——一致说明这份公开源码与运营者的声明相符,但这是一条可核对的声明,不是密码学证明(Lambda 真正执行代码的哈希只有账号所有者能看到)。

lambda/index.mjs 下载

// 陈仓媒体中转 · Lambda 入口(Function URL,经 CloudFront /api/upload 访问)。
import { validate, presign } from './sign.mjs';

const json = (statusCode, body) => ({
  statusCode,
  headers: {
    'content-type': 'application/json',
    'cache-control': 'no-store',
    'x-cc-source-sha256': process.env.SOURCE_SHA256 ?? 'unknown',
  },
  body: JSON.stringify(body),
});

export async function handler(event) {
  if (event?.requestContext?.http?.method !== 'GET') return json(405, { error: 'GET only' });
  const v = validate(event.queryStringParameters ?? {});
  if (!v.ok) return json(v.status, { error: v.error });
  const url = await presign({ bucket: process.env.BUCKET, region: process.env.AWS_REGION, blobId: v.blobId, byteLen: v.byteLen });
  return json(200, { url, expires_in: 300 });
}

lambda/sign.mjs 下载

// 陈仓媒体中转 · 上传签名。
// 服务器只签发「往 b/<blob_id> 放一个指定长度的新对象」的 5 分钟许可;
// 它看不到任何密钥,也不记录任何请求。全文公开在各分发的 /source 页。
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

export const LIMITS = { 1: 2097152, 2: 2097152, 3: 31457280 }; // 语音 / 图片 / 视频(.cca 总字节)
export const MIN_BYTE_LEN = 50; // 最小合法 .cca:34 字节头 + 16 字节 AEAD tag(= core payload::MEDIA_MIN_BLOB_LEN)
const BLOB_ID = /^[A-Za-z0-9_-]{22}$/;
const POSITIVE_INT = /^[1-9][0-9]{0,9}$/;

export function validate(q = {}) {
  if (typeof q.blob_id !== 'string' || !BLOB_ID.test(q.blob_id)) return { ok: false, status: 400, error: 'bad blob_id' };
  if (!['1', '2', '3'].includes(q.kind)) return { ok: false, status: 400, error: 'bad kind' };
  if (typeof q.byte_len !== 'string' || !POSITIVE_INT.test(q.byte_len)) return { ok: false, status: 400, error: 'bad byte_len' };
  const kind = Number(q.kind);
  const byteLen = Number(q.byte_len);
  if (byteLen < MIN_BYTE_LEN) return { ok: false, status: 400, error: 'too small' };
  if (byteLen > LIMITS[kind]) return { ok: false, status: 413, error: 'too large' };
  return { ok: true, blobId: q.blob_id, byteLen, kind };
}

export async function presign({ bucket, region, blobId, byteLen, credentials }) {
  const client = new S3Client({ region, ...(credentials ? { credentials } : {}) });
  const cmd = new PutObjectCommand({
    Bucket: bucket,
    Key: `b/${blobId}`,
    ContentLength: byteLen,
    ContentType: 'application/octet-stream',
    IfNoneMatch: '*',
  });
  return getSignedUrl(client, cmd, {
    expiresIn: 300,
    signableHeaders: new Set(['content-length', 'content-type', 'if-none-match']),
    unhoistableHeaders: new Set(['if-none-match']),
  });
}

template.yaml 下载

AWSTemplateFormatVersion: '2010-09-09'
Description: "chencang rich-media relay (bucket + upload signer). Source: see the source page served by the distributions (/source)"
Parameters:
  CodeBucket: { Type: String }
  CodeKey: { Type: String }
  SourceSha256: { Type: String }
  DistributionArns: { Type: CommaDelimitedList }   # three CloudFront distributions (ARNs, comma separated)
Resources:
  MediaBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: chencang-media
      PublicAccessBlockConfiguration: { BlockPublicAcls: true, BlockPublicPolicy: true, IgnorePublicAcls: true, RestrictPublicBuckets: true }
      OwnershipControls: { Rules: [ { ObjectOwnership: BucketOwnerEnforced } ] }
      BucketEncryption: { ServerSideEncryptionConfiguration: [ { ServerSideEncryptionByDefault: { SSEAlgorithm: AES256 } } ] }
      LifecycleConfiguration:
        Rules:
          - { Id: expire-1d, Status: Enabled, Prefix: b/, ExpirationInDays: 1, AbortIncompleteMultipartUpload: { DaysAfterInitiation: 1 } }
  MediaBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref MediaBucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal: { Service: cloudfront.amazonaws.com }
            Action: s3:GetObject
            Resource: !Sub '${MediaBucket.Arn}/b/*'
            Condition: { StringEquals: { AWS:SourceArn: !Ref DistributionArns } }
  SignerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement: [ { Effect: Allow, Principal: { Service: lambda.amazonaws.com }, Action: sts:AssumeRole } ]
      Policies:
        - PolicyName: put-media-only
          PolicyDocument:
            Statement: [ { Effect: Allow, Action: s3:PutObject, Resource: !Sub '${MediaBucket.Arn}/b/*' } ]
  # 故意不挂 AWSLambdaBasicExecutionRole:函数没有写 CloudWatch 日志的权限,想记也记不了。
  Signer:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: chencang-media-signer
      Runtime: nodejs22.x
      Handler: index.handler
      Role: !GetAtt SignerRole.Arn
      MemorySize: 256
      Timeout: 5
      Code: { S3Bucket: !Ref CodeBucket, S3Key: !Ref CodeKey }
      Environment: { Variables: { BUCKET: !Ref MediaBucket, SOURCE_SHA256: !Ref SourceSha256 } }
  SignerUrl:
    Type: AWS::Lambda::Url
    Properties: { TargetFunctionArn: !Ref Signer, AuthType: AWS_IAM }
  SignerUrlPermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref Signer
      Action: lambda:InvokeFunctionUrl
      Principal: cloudfront.amazonaws.com
      # SourceAccount instead of SourceArn: admits any CloudFront distribution in this account
      # (needed so all three mirror distributions can invoke the signer without per-ARN permissions).
      SourceAccount: !Ref AWS::AccountId
      FunctionUrlAuthType: AWS_IAM
  SignerInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !Ref Signer
      Action: lambda:InvokeFunction
      Principal: cloudfront.amazonaws.com
      # SourceAccount instead of SourceArn: admits any CloudFront distribution in this account
      # (needed so all three mirror distributions can invoke the signer without per-ARN permissions).
      SourceAccount: !Ref AWS::AccountId
  MediaOac:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig: { Name: chencang-media-s3, OriginAccessControlOriginType: s3, SigningBehavior: always, SigningProtocol: sigv4 }
  SignerOac:
    Type: AWS::CloudFront::OriginAccessControl
    Properties:
      OriginAccessControlConfig: { Name: chencang-media-signer, OriginAccessControlOriginType: lambda, SigningBehavior: always, SigningProtocol: sigv4 }
  # /b/* response headers: force every downloaded blob to be treated as an
  # opaque, non-executable download — no matter what Content-Type the
  # uploader's client claimed when it PUT the object (see F1: sign.mjs pins
  # Content-Type into the presigned request, this policy pins it again on
  # the way out so a stale/pre-fix object can't serve as text/html either).
  MediaResponseHeaders:
    Type: AWS::CloudFront::ResponseHeadersPolicy
    Properties:
      ResponseHeadersPolicyConfig:
        Name: chencang-media-blob
        Comment: "/b/* only: opaque octet-stream download, never renderable as HTML/JS"
        SecurityHeadersConfig:
          ContentTypeOptions: { Override: true }
          ContentSecurityPolicy:
            ContentSecurityPolicy: "sandbox; default-src 'none'"
            Override: true
        CustomHeadersConfig:
          Items:
            - { Header: Content-Type, Value: application/octet-stream, Override: true }
            - { Header: Content-Disposition, Value: attachment, Override: true }
  # /b/* cache TTL: short and fixed (not CachingOptimized's viewer-driven
  # long TTL) — blob objects are one-shot and expire out of S3 in ~1-2 days
  # anyway (see F3), so there is no reason to cache them for the default
  # policy's up-to-a-year ceiling.
  MediaCachePolicy:
    Type: AWS::CloudFront::CachePolicy
    Properties:
      CachePolicyConfig:
        Name: chencang-media-blob-cache
        Comment: "/b/* only: fixed 1h TTL, no cookies/headers/query in the cache key"
        DefaultTTL: 3600
        MaxTTL: 3600
        MinTTL: 0
        ParametersInCacheKeyAndForwardedToOrigin:
          CookiesConfig: { CookieBehavior: none }
          HeadersConfig: { HeaderBehavior: none }
          QueryStringsConfig: { QueryStringBehavior: none }
          EnableAcceptEncodingGzip: false
          EnableAcceptEncodingBrotli: false
Outputs:
  BucketRegionalDomain: { Value: !GetAtt MediaBucket.RegionalDomainName }
  SignerUrlDomain: { Value: !Select [ 2, !Split [ '/', !GetAtt SignerUrl.FunctionUrl ] ] }
  MediaOacId: { Value: !GetAtt MediaOac.Id }
  SignerOacId: { Value: !GetAtt SignerOac.Id }
  MediaResponseHeadersPolicyId: { Value: !GetAtt MediaResponseHeaders.Id }
  MediaCachePolicyId: { Value: !GetAtt MediaCachePolicy.Id }

waf.yaml 下载

AWSTemplateFormatVersion: '2010-09-09'
Description: "chencang media upload rate limit (CloudFront scope, us-east-1). Source: see /source on the distributions"
Resources:
  Acl:
    Type: AWS::WAFv2::WebACL
    Properties:
      Name: chencang-media-upload
      Scope: CLOUDFRONT
      DefaultAction: { Allow: {} }
      VisibilityConfig: { SampledRequestsEnabled: false, CloudWatchMetricsEnabled: true, MetricName: chencang-media-upload }
      Rules:
        - Name: upload-rate-per-ip
          Priority: 0
          Action: { Block: { CustomResponse: { ResponseCode: 429 } } }
          VisibilityConfig: { SampledRequestsEnabled: false, CloudWatchMetricsEnabled: true, MetricName: upload-rate-per-ip }
          Statement:
            RateBasedStatement:
              Limit: 60
              EvaluationWindowSec: 300
              AggregateKeyType: IP
              ScopeDownStatement:
                ByteMatchStatement:
                  FieldToMatch: { UriPath: {} }
                  PositionalConstraint: STARTS_WITH
                  SearchString: /api/upload
                  TextTransformations: [ { Priority: 0, Type: NONE } ]
Outputs:
  AclArn: { Value: !GetAtt Acl.Arn }

cf-router.js 下载

// CloudFront Function(viewer-request):落地页与源码页的静态路由。
function handler(event) {
  var r = event.request;
  if (r.uri === '/m' || r.uri.indexOf('/m/') === 0) { r.uri = '/m/index.html'; }
  else if (r.uri === '/p' || r.uri.indexOf('/p/') === 0) { r.uri = '/p/index.html'; }
  else if (r.uri === '/source' || r.uri === '/source/') { r.uri = '/source/index.html'; }
  return r;
}

dist-patch.jq 下载

.DistributionConfig
| .WebACLId = $acl
| .Origins.Items = ([.Origins.Items[] | select(.Id != "s3-chencang-media" and .Id != "lambda-media-signer")] + [
    { Id: "s3-chencang-media", DomainName: $bd, OriginPath: "", CustomHeaders: {Quantity: 0},
      S3OriginConfig: {OriginAccessIdentity: ""}, OriginAccessControlId: $mo,
      ConnectionAttempts: 3, ConnectionTimeout: 10, OriginShield: {Enabled: false} },
    { Id: "lambda-media-signer", DomainName: $sd, OriginPath: "", CustomHeaders: {Quantity: 0},
      CustomOriginConfig: {HTTPPort: 80, HTTPSPort: 443, OriginProtocolPolicy: "https-only",
        OriginSslProtocols: {Quantity: 1, Items: ["TLSv1.2"]}, OriginReadTimeout: 10, OriginKeepaliveTimeout: 5},
      OriginAccessControlId: $so, ConnectionAttempts: 3, ConnectionTimeout: 10, OriginShield: {Enabled: false} }
  ])
| .Origins.Quantity = (.Origins.Items | length)
| def beh($p; $origin; $cache; $methods; $fn; $rhp):
    { PathPattern: $p, TargetOriginId: $origin, ViewerProtocolPolicy: "redirect-to-https",
      AllowedMethods: {Quantity: ($methods|length), Items: $methods, CachedMethods: {Quantity: 2, Items: ["GET","HEAD"]}},
      Compress: false, SmoothStreaming: false, FieldLevelEncryptionId: "",
      CachePolicyId: $cache,
      ResponseHeadersPolicyId: $rhp,
      OriginRequestPolicyId: (if $origin == "lambda-media-signer" then "b689b0a8-53d0-40ab-baf2-68738e2966ac" else null end),
      TrustedSigners: {Enabled: false, Quantity: 0}, TrustedKeyGroups: {Enabled: false, Quantity: 0},
      LambdaFunctionAssociations: {Quantity: 0},
      FunctionAssociations: (if $fn then {Quantity: 1, Items: [{EventType: "viewer-request", FunctionARN: $fn}]} else {Quantity: 0} end) }
    | with_entries(select(.value != null));
  .CacheBehaviors.Items = ([(.CacheBehaviors.Items // [])[] | select(.PathPattern | IN("/b/*","/api/upload","/m/*","/p","/p/*","/source*") | not)] + [
    beh("/api/upload"; "lambda-media-signer"; "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"; ["GET","HEAD"]; null; null),
    beh("/b/*";        "s3-chencang-media";   $cp; ["GET","HEAD"]; null; $rhp),
    beh("/m/*";        "s3-chencang-site";    "658327ea-f89d-4fab-a63d-7e88639e58f6"; ["GET","HEAD"]; $fn; null),
    beh("/p";          "s3-chencang-site";    "658327ea-f89d-4fab-a63d-7e88639e58f6"; ["GET","HEAD"]; $fn; null),
    beh("/p/*";        "s3-chencang-site";    "658327ea-f89d-4fab-a63d-7e88639e58f6"; ["GET","HEAD"]; $fn; null),
    beh("/source*";    "s3-chencang-site";    "4135ea2d-6df8-44a3-9df3-4b5a84be39ad"; ["GET","HEAD"]; $fn; null)
  ])
| .CacheBehaviors.Quantity = (.CacheBehaviors.Items | length)

deploy.sh 下载

#!/usr/bin/env bash
# 陈仓媒体中转部署。全文公开在各分发的 /source 页(如 https://d3nnqcgewrb4f0.cloudfront.net/source)。
set -euo pipefail
cd "$(dirname "$0")"
REGION=ap-northeast-1
# 三个同配置分发:H1(主)、H2、H3(镜像,见 create-mirror-distribution.sh)
DIST_IDS=(E2TRN129L7BYBK E3MDHL18OSE5DL E3P8OZYVOPB9EJ)
ACCOUNT=186180737711
DIST_ARNS=""; ARN_JSON="[]"
for id in "${DIST_IDS[@]}"; do
  arn="arn:aws:cloudfront::${ACCOUNT}:distribution/${id}"
  DIST_ARNS="${DIST_ARNS:+$DIST_ARNS,}$arn"
  ARN_JSON=$(jq -c --arg a "$arn" '. + [$a]' <<<"$ARN_JSON")
done
CODE_BUCKET=chencang-site          # 复用已有私有桶存放部署包,键在 _deploy/ 下(CloudFront 默认行为可读,内容本就公开)

# 1) 可复现打包:固定 mtime + 固定顺序 + 去掉额外属性
rm -rf build && mkdir -p build/pkg
cp lambda/index.mjs lambda/sign.mjs build/pkg/
TZ=UTC touch -t 198001010000 build/pkg/*
(cd build/pkg && TZ=UTC zip -X -D -q ../lambda.zip index.mjs sign.mjs)
SHA=$(shasum -a 256 build/lambda.zip | cut -d' ' -f1)
echo "$SHA" > build/SOURCE_SHA256
KEY="_deploy/media-signer-${SHA}.zip"
aws s3 cp build/lambda.zip "s3://${CODE_BUCKET}/${KEY}" --region "$REGION" >/dev/null

# 2) 主栈 + WAF 栈
aws cloudformation deploy --region "$REGION" --stack-name chencang-media --template-file template.yaml \
  --capabilities CAPABILITY_IAM --no-fail-on-empty-changeset \
  --parameter-overrides CodeBucket="$CODE_BUCKET" CodeKey="$KEY" SourceSha256="$SHA" DistributionArns="$DIST_ARNS"
aws cloudformation deploy --region us-east-1 --stack-name chencang-media-waf --template-file waf.yaml --no-fail-on-empty-changeset
out() { aws cloudformation describe-stacks --region "$1" --stack-name "$2" --query "Stacks[0].Outputs[?OutputKey=='$3'].OutputValue" --output text; }
BUCKET_DOM=$(out "$REGION" chencang-media BucketRegionalDomain)
SIGNER_DOM=$(out "$REGION" chencang-media SignerUrlDomain)
MEDIA_OAC=$(out "$REGION" chencang-media MediaOacId)
SIGNER_OAC=$(out "$REGION" chencang-media SignerOacId)
MEDIA_RHP=$(out "$REGION" chencang-media MediaResponseHeadersPolicyId)
MEDIA_CP=$(out "$REGION" chencang-media MediaCachePolicyId)
ACL_ARN=$(out us-east-1 chencang-media-waf AclArn)

# 3) CloudFront Function(发布 LIVE)
FN=chencang-site-router
if aws cloudfront describe-function --name "$FN" >/dev/null 2>&1; then
  ETAG=$(aws cloudfront describe-function --name "$FN" --query ETag --output text)
  ETAG=$(aws cloudfront update-function --name "$FN" --if-match "$ETAG" --function-config Comment=chencang,Runtime=cloudfront-js-2.0 --function-code fileb://cf-router.js --query ETag --output text)
else
  ETAG=$(aws cloudfront create-function --name "$FN" --function-config Comment=chencang,Runtime=cloudfront-js-2.0 --function-code fileb://cf-router.js --query ETag --output text)
fi
aws cloudfront publish-function --name "$FN" --if-match "$ETAG" >/dev/null
FN_ARN=$(aws cloudfront describe-function --name "$FN" --stage LIVE --query FunctionSummary.FunctionMetadata.FunctionARN --output text)

# 4a) chencang-site 桶策略(不在模板里):幂等地让三个分发都能读,保留其他语句
SITE_POLICY=$(aws s3api get-bucket-policy --bucket "$CODE_BUCKET" --query Policy --output text)
jq --argjson arns "$ARN_JSON" '
  .Statement |= map(if .Sid == "AllowCloudFrontServicePrincipalReadOnly"
                    then .Condition.StringEquals["AWS:SourceArn"] = $arns else . end)' <<<"$SITE_POLICY" > build/site-policy.json
jq -e '[.Statement[] | select(.Sid == "AllowCloudFrontServicePrincipalReadOnly")] | length == 1' build/site-policy.json >/dev/null \
  || { echo "site bucket policy lacks the AllowCloudFrontServicePrincipalReadOnly statement" >&2; exit 1; }
aws s3api put-bucket-policy --bucket "$CODE_BUCKET" --policy file://build/site-policy.json

# 4b) 幂等地给每个分发追加两个源站 + 六条行为 + WAF(默认行为、别名、证书不动)
for DIST_ID in "${DIST_IDS[@]}"; do
  aws cloudfront get-distribution-config --id "$DIST_ID" > "build/dist-${DIST_ID}.json"
  DETAG=$(jq -r .ETag "build/dist-${DIST_ID}.json")
  jq --arg bd "$BUCKET_DOM" --arg sd "$SIGNER_DOM" --arg mo "$MEDIA_OAC" --arg so "$SIGNER_OAC" \
     --arg fn "$FN_ARN" --arg acl "$ACL_ARN" --arg rhp "$MEDIA_RHP" --arg cp "$MEDIA_CP" \
     -f dist-patch.jq "build/dist-${DIST_ID}.json" > "build/dist-new-${DIST_ID}.json"
  aws cloudfront update-distribution --id "$DIST_ID" --if-match "$DETAG" --distribution-config "file://build/dist-new-${DIST_ID}.json" >/dev/null
done
for DIST_ID in "${DIST_IDS[@]}"; do aws cloudfront wait distribution-deployed --id "$DIST_ID"; done

# 4c) 三台一致性核对:规范化后 diff,非空即失败
NORM='.DistributionConfig | {
  WebACLId,
  Origins: [.Origins.Items[] | {Id, DomainName, OAC: .OriginAccessControlId}] | sort_by(.Id),
  CacheBehaviors: [(.CacheBehaviors.Items // [])[] | {PathPattern, TargetOriginId, CachePolicyId, ResponseHeadersPolicyId, FunctionAssociations}] | sort_by(.PathPattern),
  Default: .DefaultCacheBehavior.TargetOriginId }'
for DIST_ID in "${DIST_IDS[@]}"; do
  aws cloudfront get-distribution-config --id "$DIST_ID" | jq -S "$NORM" > "build/norm-${DIST_ID}.json"
  echo "== ${DIST_ID}"; cat "build/norm-${DIST_ID}.json"
done
for DIST_ID in "${DIST_IDS[@]:1}"; do
  diff "build/norm-${DIST_IDS[0]}.json" "build/norm-${DIST_ID}.json" || { echo "DISTRIBUTION CONFIG DRIFT: ${DIST_IDS[0]} vs ${DIST_ID}" >&2; exit 1; }
done
echo "distributions identical"

# 5) 发布源码页与落地页
node gen-source-page.mjs
aws s3 sync build/source "s3://chencang-site/source" --delete --cache-control "no-cache" --region "$REGION"
aws s3 cp build/source/files "s3://chencang-site/source/files" --recursive --content-type "text/plain; charset=utf-8" --cache-control "no-cache" --region "$REGION"
aws s3 cp ../../site/m/index.html "s3://chencang-site/m/index.html" --content-type "text/html; charset=utf-8" --cache-control "max-age=300" --region "$REGION"
aws s3 cp ../../site/p/index.html "s3://chencang-site/p/index.html" --content-type "text/html; charset=utf-8" --cache-control "max-age=300" --region "$REGION"

echo "deployed; SOURCE_SHA256=$SHA"

create-mirror-distribution.sh 下载

#!/usr/bin/env bash
# 克隆主分发 E2TRN129L7BYBK 为一个镜像分发:源站、行为、策略、函数、WAF 全同,
# 只去掉自定义域名、改用 *.cloudfront.net 默认证书。用法:./create-mirror-distribution.sh <N>
# 建好后把新 ID 加进 deploy.sh 的 DIST_IDS,再跑 ./deploy.sh 让三台配置对齐。
set -euo pipefail
N=${1:?usage: create-mirror-distribution.sh <N>}
SRC=E2TRN129L7BYBK
TMP=$(mktemp)
trap 'rm -f "$TMP"' EXIT
aws cloudfront get-distribution-config --id "$SRC" --query DistributionConfig --output json \
  | jq --arg ref "chencang-mirror-${N}-$(date +%s)" --arg c "chencang mirror ${N}" '
      .CallerReference = $ref
      | .Comment = $c
      | .Aliases = {Quantity: 0}
      | .ViewerCertificate = {CloudFrontDefaultCertificate: true, MinimumProtocolVersion: "TLSv1", CertificateSource: "cloudfront"}' \
  > "$TMP"
aws cloudfront create-distribution --distribution-config "file://$TMP" \
  --query 'Distribution.{Id:Id,Domain:DomainName,Status:Status}' --output table

gen-source-page.mjs 下载

// 生成 https://d3nnqcgewrb4f0.cloudfront.net/source :逐文件展示媒体中转的全部源码。
import { readFileSync, writeFileSync, mkdirSync, copyFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';

const here = dirname(fileURLToPath(import.meta.url));
const FILES = ['lambda/index.mjs', 'lambda/sign.mjs', 'template.yaml', 'waf.yaml', 'cf-router.js', 'dist-patch.jq', 'deploy.sh', 'create-mirror-distribution.sh', 'gen-source-page.mjs'];
const sha = readFileSync(join(here, 'build/SOURCE_SHA256'), 'utf8').trim();
const esc = (s) => s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const out = join(here, 'build/source');
mkdirSync(join(out, 'files/lambda'), { recursive: true });
const sections = FILES.map((f) => {
  copyFileSync(join(here, f), join(out, 'files', f));
  const body = readFileSync(join(here, f), 'utf8');
  return `<section><h2 id="${f}">${f} <a href="/source/files/${f}" download>下载</a></h2><pre><code>${esc(body)}</code></pre></section>`;
}).join('\n');
const html = `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>陈仓中转源码</title><style>
:root{--bg:#fff;--fg:#1d1d1f;--muted:#6e6e73;--code:#f5f5f7}
@media (prefers-color-scheme:dark){:root{--bg:#111;--fg:#f5f5f7;--muted:#a1a1a6;--code:#1c1c1e}}
body{margin:0;padding:16px;max-width:880px;margin-inline:auto;background:var(--bg);color:var(--fg);font:15px/1.6 -apple-system,system-ui,sans-serif}
pre{background:var(--code);padding:12px;overflow-x:auto;border-radius:8px;font-size:12px}h2{font-size:15px;margin-top:32px}p{color:var(--muted)}
</style></head><body>
<h1>陈仓媒体中转 · 全部源码</h1>
<p>陈仓的文字消息不经过任何服务器。语音、图片、视频先在手机上加密,再以密文形式经这里中转;App 内 24 小时后显示「已过期」,云端按 S3 生命周期约 1–2 天内自动删除。服务器拿不到任何密钥,也不记录 IP。下面就是线上运行的全部代码。</p>
<p>运营者声明的线上部署包 SHA-256:<code>${sha}</code>。可核对方法:下载 lambda/index.mjs 与 lambda/sign.mjs,按 deploy.sh 第 1 步打包后计算 SHA-256,与此值以及 <code>curl -sI "https://d3nnqcgewrb4f0.cloudfront.net/api/upload" | grep x-cc-source-sha256</code> 的结果比对——一致说明这份公开源码与运营者的声明相符,但这是一条可核对的声明,不是密码学证明(Lambda 真正执行代码的哈希只有账号所有者能看到)。</p>
${sections}
</body></html>`;
writeFileSync(join(out, 'index.html'), html);
console.log(`wrote ${join(out, 'index.html')}`);