mirror of
https://github.com/readest/readest.git
synced 2026-05-20 17:47:48 +00:00
chore: support deploy in cloudflare with opennext and fix android build
This commit is contained in:
parent
0c65d44bc9
commit
ede37757db
7 changed files with 3269 additions and 44 deletions
7
.github/workflows/release.yml
vendored
7
.github/workflows/release.yml
vendored
|
|
@ -84,7 +84,7 @@ jobs:
|
|||
args: '--target aarch64-pc-windows-msvc --bundles nsis'
|
||||
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
|
@ -149,6 +149,9 @@ jobs:
|
|||
|
||||
- name: build and upload Android apks
|
||||
if: matrix.config.release == 'android'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.0.11902837
|
||||
run: |
|
||||
cd apps/readest-app/
|
||||
rm -rf src-tauri/gen/android
|
||||
|
|
@ -176,8 +179,6 @@ jobs:
|
|||
gh release upload ${{ needs.get-release.outputs.release_tag }} $universial_apk --clobber
|
||||
echo "Uploading $arm64_apk to GitHub release"
|
||||
gh release upload ${{ needs.get-release.outputs.release_tag }} $arm64_apk --clobber
|
||||
env:
|
||||
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/27.0.11902837
|
||||
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
if: matrix.config.release != 'android'
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
"@tauri-apps/plugin-shell": "~2.2.0",
|
||||
"@tauri-apps/plugin-updater": "^2.5.1",
|
||||
"@zip.js/zip.js": "^2.7.53",
|
||||
"aws4fetch": "^1.0.20",
|
||||
"clsx": "^2.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"cssbeautify": "^0.3.1",
|
||||
|
|
@ -77,6 +78,7 @@
|
|||
"zustand": "5.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@opennextjs/cloudflare": "^0.5.12",
|
||||
"@tauri-apps/cli": "2.3.1",
|
||||
"@types/cors": "^2.8.17",
|
||||
"@types/cssbeautify": "^0.3.5",
|
||||
|
|
@ -99,6 +101,7 @@
|
|||
"postcss-nested": "^7.0.2",
|
||||
"raw-loader": "^4.0.2",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.7.2"
|
||||
"typescript": "^5.7.2",
|
||||
"wrangler": "^4.4.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||
import { corsAllMethods, runMiddleware } from '@/utils/cors';
|
||||
import { createSupabaseClient } from '@/utils/supabase';
|
||||
import { validateUserAndToken } from '@/utils/access';
|
||||
import { DeleteObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { s3Client } from '@/utils/s3';
|
||||
import { deleteObject } from '@/utils/r2';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
await runMiddleware(req, res, corsAllMethods);
|
||||
|
|
@ -41,13 +40,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
return res.status(403).json({ error: 'Unauthorized access to the file' });
|
||||
}
|
||||
|
||||
const deleteCommand = new DeleteObjectCommand({
|
||||
Bucket: process.env['R2_BUCKET_NAME'] || '',
|
||||
Key: fileKey,
|
||||
});
|
||||
|
||||
try {
|
||||
await s3Client.send(deleteCommand);
|
||||
await deleteObject(process.env['R2_BUCKET_NAME'] || '', fileKey);
|
||||
const { error: deleteError } = await supabase.from('files').delete().eq('id', fileRecord.id);
|
||||
|
||||
if (deleteError) {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { supabase, createSupabaseClient } from '@/utils/supabase';
|
||||
import { corsAllMethods, runMiddleware } from '@/utils/cors';
|
||||
import { GetObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { s3Client } from '@/utils/s3';
|
||||
import { getDownloadSignedUrl } from '@/utils/r2';
|
||||
|
||||
const getUserAndToken = async (authHeader: string | undefined) => {
|
||||
if (!authHeader) return {};
|
||||
|
|
@ -56,15 +54,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
return res.status(403).json({ error: 'Unauthorized access to the file' });
|
||||
}
|
||||
|
||||
const getCommand = new GetObjectCommand({
|
||||
Bucket: process.env['R2_BUCKET_NAME'] || '',
|
||||
Key: fileKey,
|
||||
});
|
||||
|
||||
try {
|
||||
const downloadUrl = await getSignedUrl(s3Client, getCommand, {
|
||||
expiresIn: 1800,
|
||||
});
|
||||
const bucketName = process.env['R2_BUCKET_NAME'] || '';
|
||||
const downloadUrl = await getDownloadSignedUrl(bucketName, fileKey, 1800);
|
||||
|
||||
res.status(200).json({
|
||||
downloadUrl,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { supabase, createSupabaseClient } from '@/utils/supabase';
|
||||
import { corsAllMethods, runMiddleware } from '@/utils/cors';
|
||||
import { PutObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { getStoragePlanData } from '@/utils/access';
|
||||
import { s3Client } from '@/utils/s3';
|
||||
import { getUploadSignedUrl } from '@/utils/r2';
|
||||
|
||||
const getUserAndToken = async (authHeader: string | undefined) => {
|
||||
if (!authHeader) return {};
|
||||
|
|
@ -75,19 +73,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
if (insertError) return res.status(500).json({ error: insertError.message });
|
||||
}
|
||||
|
||||
const signableHeaders = new Set<string>();
|
||||
signableHeaders.add('content-length');
|
||||
const putCommand = new PutObjectCommand({
|
||||
Bucket: process.env['R2_BUCKET_NAME'] || '',
|
||||
Key: objectKey,
|
||||
ContentLength: objSize,
|
||||
});
|
||||
|
||||
try {
|
||||
const uploadUrl = await getSignedUrl(s3Client, putCommand, {
|
||||
expiresIn: 1800,
|
||||
signableHeaders,
|
||||
});
|
||||
const uploadUrl = await getUploadSignedUrl(
|
||||
process.env['R2_BUCKET_NAME'] || '',
|
||||
objectKey,
|
||||
objSize,
|
||||
1800,
|
||||
);
|
||||
|
||||
res.status(200).json({
|
||||
uploadUrl,
|
||||
|
|
|
|||
60
apps/readest-app/src/utils/r2.ts
Normal file
60
apps/readest-app/src/utils/r2.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import { AwsClient } from 'aws4fetch';
|
||||
|
||||
const getR2Client = () => {
|
||||
return new AwsClient({
|
||||
service: 's3',
|
||||
region: process.env['R2_REGION'] || 'auto',
|
||||
accessKeyId: process.env['R2_ACCESS_KEY_ID']!,
|
||||
secretAccessKey: process.env['R2_SECRET_ACCESS_KEY']!,
|
||||
});
|
||||
};
|
||||
|
||||
const getR2Url = () => {
|
||||
const R2_ACCOUNT_ID = process.env['R2_ACCOUNT_ID']!;
|
||||
return `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`;
|
||||
};
|
||||
|
||||
export const getDownloadSignedUrl = async (
|
||||
bucketName: string,
|
||||
fileKey: string,
|
||||
expiresIn: number,
|
||||
) => {
|
||||
return (
|
||||
await getR2Client().sign(
|
||||
new Request(`${getR2Url()}/${bucketName}/${fileKey}?X-Amz-Expires=${expiresIn}`),
|
||||
{
|
||||
aws: { signQuery: true },
|
||||
},
|
||||
)
|
||||
).url.toString();
|
||||
};
|
||||
|
||||
export const getUploadSignedUrl = async (
|
||||
bucketName: string,
|
||||
fileKey: string,
|
||||
contentLength: number,
|
||||
expiresIn: number,
|
||||
) => {
|
||||
return (
|
||||
await getR2Client().sign(
|
||||
new Request(
|
||||
`${getR2Url()}/${bucketName}/${fileKey}?X-Amz-Expires=${expiresIn}&X-Amz-SignedHeaders=content-length`,
|
||||
{
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Length': contentLength.toString(),
|
||||
},
|
||||
},
|
||||
),
|
||||
{
|
||||
aws: { signQuery: true },
|
||||
},
|
||||
)
|
||||
).url.toString();
|
||||
};
|
||||
|
||||
export const deleteObject = async (bucketName: string, fileKey: string) => {
|
||||
return await getR2Client().fetch(`${getR2Url()}/${bucketName}/${fileKey}`, {
|
||||
method: 'DELETE',
|
||||
});
|
||||
};
|
||||
3195
pnpm-lock.yaml
generated
3195
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue