Skip to content

Switch to new REST object APIs #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
REACT_APP_NEOFS=""
REACT_APP_NETMAP_CONTRACT="0xc4576ea5c3081dd765a17aaaa73d9352e74bdc28"
67 changes: 40 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ Send.NeoFS is a simple example of integration with NeoFS network via HTTP protoc

Set variables in the `.env` file before executing the commands:
- `REACT_APP_NEOFS` - Path to SendFS
- `REACT_APP_CONTAINER_ID` - NeoFS container ID where the objects would be stored
- `REACT_APP_NETMAP_CONTRACT` - NeoFS netmap contract

# Send.NeoFS local up

Expand Down Expand Up @@ -69,78 +67,93 @@ password: <secret>
proxy_cache_path /srv/neofs_cache/ levels=1:2 keys_zone=neofs_cache:50m max_size=16g inactive=60m use_temp_path=off;

server {
set $cid HPUKdZBBtD75jDN8iVb3zoaNACWinuf1vF5kkYpMMbap;
set $data_cid 41tVWBvQVTLGQPHBmXySHsJkcc6X17i39bMuJe9wYhAJ;
set $neofs_http_gateway http.fs.neo.org;
set $cid 7CpJVtBdNvPjjdYwV7q9CghGVPagVLTs71BPQuGQLKSQ;
set $data_cid 754iyTDY8xUtZJZfheSYLUn7jvCkxr79RcbjMt81QykC;
set $neofs_rest_gateway https://rest.fs.neo.org;
set $rpc rpc.morph.fs.neo.org:40341;
client_max_body_size 100m;
proxy_connect_timeout 5m;
proxy_send_timeout 5m;
proxy_read_timeout 5m;
send_timeout 5m;
default_type application/octet-stream;

location ~ "^\/chain" {
rewrite ^/chain/(.*) /$1 break;
proxy_pass https://rpc;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429 non_idempotent;
}

location /gate/objects/ {
rewrite ^/gate/(.*) /v1/objects/$data_cid break;
proxy_pass $neofs_rest_gateway;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429;
proxy_pass_request_headers on;
}

location /gate/upload/ {
rewrite ^/gate/(.*) /upload/$data_cid break;
proxy_pass https://$neofs_http_gateway;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%s/neofs_http_gateway/neofs_rest_gateway/g

rewrite ^/gate/(.*) /v1/upload/$data_cid break;
proxy_pass $neofs_rest_gateway;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429;
proxy_pass_request_headers on;
proxy_set_header X-Attribute-email $http_x_attribute_email;
proxy_set_header X-Attribute-NEOFS-Expiration-Epoch $http_x_attribute_neofs_expiration_epoch;
}

location ~ "^\/gate\/get(/.*)?\/?$" {
rewrite ^/gate/get/(.*) /$data_cid/$1 break;
proxy_pass https://$neofs_http_gateway;
rewrite ^/gate/get/(.*) /v1/get/$data_cid/$1 break;
proxy_pass $neofs_rest_gateway;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429;

proxy_intercept_errors on;
proxy_cache_valid 404 0;
proxy_cache_valid 200 15m;
proxy_buffering on;
proxy_cache neofs_cache;
proxy_cache_methods GET;
}

location /signup_google/ {
proxy_pass http://localhost:8084/login?service=google;
proxy_intercept_errors on;
proxy_buffering on;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location /signup_github/ {
proxy_pass http://localhost:8084/login?service=github;
proxy_intercept_errors on;
proxy_buffering on;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}

location ~ "^\/callback" {
rewrite ^/callback\?(.*) /$1 break;
proxy_pass http://localhost:8084;
}

location /load {
rewrite '^(/.*)$' /get_by_attribute/$cid/FileName/index.html break;
proxy_pass https://$neofs_http_gateway;
rewrite '^(/.*)$' /v1/objects/$cid/by_attribute/FileName/index.html break;
proxy_pass $neofs_rest_gateway;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429;
include /etc/nginx/mime.types;
}

location /toc {
rewrite '^(/.*)$' /get_by_attribute/$cid/FileName/index.html break;
proxy_pass https://$neofs_http_gateway;
rewrite '^(/.*)$' /v1/objects/$cid/by_attribute/FileName/index.html break;
proxy_pass $neofs_rest_gateway;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_403 http_429;
include /etc/nginx/mime.types;
}

location / {
rewrite '^(/[0-9a-zA-Z\-]{43,44})$' /get/$cid/$1 break;
rewrite '^/$' /get_by_attribute/$cid/FileName/index.html break;
rewrite '^/([^/]*)$' /get_by_attribute/$cid/FileName/$1 break;
rewrite '^(/.*)$' /get_by_attribute/$cid/FilePath/$1 break;
proxy_pass https://$neofs_http_gateway;
proxy_pass https://$neofs_rest_gateway;
}
}
```
Expand Down
4 changes: 0 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,6 @@ library.add(
interface Environment {
version: string | undefined
server: string | undefined
netmapContract: string | undefined
epochLine: string | undefined
}

interface User {
Expand Down Expand Up @@ -76,8 +74,6 @@ export const App = () => {
const [environment] = useState<Environment>({
version: process.env.REACT_APP_VERSION,
server: process.env.REACT_APP_NEOFS,
netmapContract: process.env.REACT_APP_NETMAP_CONTRACT,
epochLine: "c25hcHNob3RFcG9jaA==",
});
const [user] = useState<User | null>(getCookie('X-Bearer') && getCookie('X-Attribute-Email') ? {
XBearer: getCookie('X-Bearer'),
Expand Down
68 changes: 18 additions & 50 deletions src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,6 @@ import {
Button,
} from 'react-bulma-components';

function base64ToArrayBuffer(base64: string) {
const bytes: any = new Uint8Array([0, 0, 0, 0]);
const binary_string: string = base64;
const len: number = binary_string.length;
for (let i: number = 0; i < len; i += 1) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
}

const Home = ({
onModal,
onScroll,
Expand All @@ -36,7 +26,7 @@ const Home = ({
}) => {
const [files, setFiles] = useState<File[]>([]);
const [dragActive, setDragActive] = useState<boolean>(false);
const [lifetimeData, setLifetimeData] = useState<number>(12);
const [lifetimeData, setLifetimeData] = useState<string>('12h');
const [isLoading, setLoading] = useState<boolean>(false);
const [isCopied, setCopy] = useState<boolean | string>(false);
const fileUploadMbLimit: number = 200 * 1024 * 1024;
Expand Down Expand Up @@ -106,48 +96,26 @@ const Home = ({
}

setLoading(true);
api('POST', `/chain`, {
"id":"1",
"jsonrpc":"2.0",
"method":"getstorage",
"params":[
environment.netmapContract,
environment.epochLine,
],
}).then((res: any) => {
if (res['result']) {
const epoch_b64: any = atob(res['result']);
const epoch_bytes: any = base64ToArrayBuffer(epoch_b64);
const dv2: any = new DataView(epoch_bytes.buffer);
const epoch: string | number = dv2.getUint32(0, true);

files.forEach((file: File) => {
const formData: any = new FormData();
formData.append('file', file);
onUploadFile(formData, file.name, epoch, lifetimeData);
});
} else {
setLoading(false);
onModal('failed', 'Something went wrong, try again');
}
}).catch(() => {
setLoading(false);
onModal('failed', 'Something went wrong, try again');
files.forEach((file: File) => {
onUploadFile(file);
});
e.preventDefault();
};

const onUploadFile = (formData: any, filename: string | null, epoch: string | number, lifetime: string | number) => {
document.cookie = `Bearer=${user.XBearer}; path=/gate/upload; expires=${new Date(Date.now() + 10 * 1000).toUTCString()}`;
api('POST', '/gate/upload/', formData, {
'X-Attribute-NEOFS-Expiration-Epoch': String(Number(epoch) + Number(lifetime)),
'X-Attribute-email': user.XAttributeEmail,
'Content-Type': 'multipart/form-data',
const onUploadFile = (file: any | null) => {
document.cookie = `Bearer=${user.XBearer}; path=/gate/objects; expires=${new Date(Date.now() + 10 * 1000).toUTCString()}`;
api('POST', "/gate/objects/", file, {
'X-Attributes': JSON.stringify({
'FileName': file.name,
'Email': user.XAttributeEmail,
}),
'X-Neofs-Expiration-Duration': lifetimeData,
'Content-Type': file.type,
}).then((res: any) => {
res['filename'] = filename;
res['filename'] = file.name;
setUploadedObjects((uploadedObjectsTemp: UploadedObject[]) => [...uploadedObjectsTemp, res]);
setFiles((files: File[]) => {
const filesTemp = files.filter((item: File) => item.name !== filename);
const filesTemp = files.filter((item: File) => item.name !== file.name);
if (filesTemp.length === 0) {
setLoading(false);
}
Expand Down Expand Up @@ -211,10 +179,10 @@ const Home = ({
value={lifetimeData}
disabled={isLoading}
>
<option value="12">12 epochs (hours)</option>
<option value="24">24 epochs (1 day)</option>
<option value="48">48 epochs (2 days)</option>
<option value="96">96 epochs (4 days)</option>
<option value="12h">12 epochs (hours)</option>
<option value="24h">24 epochs (1 day)</option>
<option value="48h">48 epochs (2 days)</option>
<option value="96h">96 epochs (4 days)</option>
</Form.Select>
</Form.Control>
</Form.Field>
Expand Down
1 change: 1 addition & 0 deletions src/Load.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ const Load = ({
>
<Heading weight="semibold" subtitle style={{ textAlign: 'center' }}>File data</Heading>
<Heading weight="light" size={6} subtitle style={{ margin: '10px 0' }}>{`Filename: ${objectData.filename ? objectData.filename : '-'}`}</Heading>
<Heading weight="light" size={6} subtitle style={{ margin: '10px 0' }}>{`Content-Type: ${objectData.contentType ? objectData.contentType : '-'}`}</Heading>
<Heading weight="light" size={6} subtitle style={{ margin: '10px 0' }}>{`Size: ${objectData.size ? objectData.size : '-'}`}</Heading>
<Heading weight="light" size={6} subtitle style={{ margin: '10px 0' }}>{`Expiration epoch: ${objectData.expirationEpoch ? objectData.expirationEpoch : '-'}`}</Heading>
<Heading weight="light" size={6} subtitle style={{ margin: '10px 0' }}>{`Owner ID: ${objectData.ownerId ? objectData.ownerId : '-'}`}</Heading>
Expand Down
10 changes: 6 additions & 4 deletions src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface ObjectData {
containerId?: string | null
ownerId?: string | null
filename?: string | null
contentType?: string | null
size?: string | null
expirationEpoch?: string | null
}
Expand All @@ -17,9 +18,8 @@ async function serverRequest(method: Methods, url: string, params: object, heade
headers,
}

if (json['headers']['Content-Type'] === 'multipart/form-data') {
if (json['headers']['Content-Type']) {
json['body'] = params;
delete json['headers']['Content-Type'];
} else if (Object.keys(params).length > 0) {
json['body'] = JSON.stringify(params);
json['headers']['Content-Type'] = 'application/json';
Expand All @@ -45,12 +45,14 @@ export default function api(method: Methods, url: string, params: object = {}, h
if (method === 'HEAD' && response.status !== 200) {
reject(res);
} else if (method === 'HEAD' && response.headers) {
const attributes: any = response.headers.get('X-Attributes') ? JSON.parse(response.headers.get('X-Attributes')) : {};
const res: ObjectData = {
'filename': response.headers.get('X-Attribute-Filename'),
'filename': attributes['FileName'],
'contentType': response.headers.get('Content-Type'),
'containerId': response.headers.get('X-Container-Id'),
'ownerId': response.headers.get('X-Owner-Id'),
'size': response.headers.get('Content-Length') ? response.headers.get('Content-Length') : response.headers.get('x-neofs-payload-length'),
'expirationEpoch': response.headers.get('X-Attribute-Neofs-Expiration-Epoch'),
'expirationEpoch': attributes['__NEOFS__EXPIRATION_EPOCH'],
}
resolve(res);
} else if (method === 'GET' && url.indexOf(`/gate/get/`) !== -1) {
Expand Down
Loading