Skip to content

Commit

Permalink
Merge pull request #154 from uploadcare/feat/custom-cname
Browse files Browse the repository at this point in the history
feat: add `--cfg-cdn-base` to set custom cname
  • Loading branch information
nd0ut authored May 30, 2022
2 parents 803e67e + fbe8814 commit 3d6c677
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 132 deletions.
1 change: 1 addition & 0 deletions abstract/Block.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export class Block extends BaseComponent {
'--cfg-init-activity',
'--cfg-done-activity',
'--cfg-max-local-file-size-bytes',
'--cfg-cdn-cname',
];
cfgProps.forEach((prop) => {
this.bindCssData(prop);
Expand Down
4 changes: 2 additions & 2 deletions blocks/CloudImageEditor/src/CloudEditor.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Block } from '../../../abstract/Block.js';
import { createOriginalUrl } from '../../../utils/cdn-utils.js';
import { classNames } from './lib/classNames.js';
import { debounce } from './lib/debounce.js';
import { preloadImage } from './lib/preloadImage.js';
Expand Down Expand Up @@ -120,8 +121,7 @@ export class CloudEditor extends Block {
return;
}

// TODO: fix hardcode
this.$['*originalUrl'] = `https://ucarecdn.com/${this.$.uuid}/`;
this.$['*originalUrl'] = createOriginalUrl(this.$['*--cfg-cdn-cname'], this.$.uuid);

fetch(`${this.$['*originalUrl']}-/json/`)
.then((response) => response.json())
Expand Down
4 changes: 2 additions & 2 deletions blocks/CloudImageEditor/src/EditorImageCropper.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function validateCrop(crop) {
* }} State
*/

/** @extends {Block<State>} */
/** @extends {Block<State & import('./CloudEditor.js').State>} */
export class EditorImageCropper extends Block {
init$ = {
image: null,
Expand Down Expand Up @@ -464,7 +464,7 @@ export class EditorImageCropper extends Block {
.then(() => image)
.catch((err) => {
console.error('Failed to load image', { error: err });
this.$['*networProblems'] = true;
this.$['*networkProblems'] = true;
return Promise.resolve(image);
});
}
Expand Down
12 changes: 9 additions & 3 deletions blocks/FileItem/FileItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { uploadFile } from '../../submodules/upload-client/upload-client.js';
import { UiMessage } from '../MessageBox/MessageBox.js';
import { fileCssBg } from '../svg-backgrounds/svg-backgrounds.js';
import { customUserAgent } from '../utils/userAgent.js';
import { createCdnUrl, createCdnUrlModifiers, createOriginalUrl } from '../../utils/cdn-utils.js';

/** @typedef {Partial<import('../MessageBox/MessageBox.js').State>} ExternalState */

Expand Down Expand Up @@ -174,10 +175,13 @@ export class FileItem extends Block {
this.setAttribute('loaded', '');

if (this.entry.getValue('isImage')) {
let url = `https://ucarecdn.com/${uuid}/`;
this._revokeThumbUrl();
let size = this.$['*--cfg-thumb-size'] || 76;
this.$.thumbUrl = `url(${url}-/scale_crop/${size}x${size}/center/)`;
let thumbUrl = createCdnUrl(
createOriginalUrl(this.$['*--cfg-cdn-cname'], uuid),
createCdnUrlModifiers(`scale_crop/${size}x${size}/center`)
);
this.$.thumbUrl = `url(${thumbUrl})`;
}
});

Expand All @@ -188,7 +192,8 @@ export class FileItem extends Block {
if (this.entry.getValue('isImage')) {
this._revokeThumbUrl();
let size = this.$['*--cfg-thumb-size'] || 76;
this.$.thumbUrl = `url(${cdnUrl}-/scale_crop/${size}x${size}/center/)`;
let thumbUrl = createCdnUrl(cdnUrl, createCdnUrlModifiers(`scale_crop/${size}x${size}/center`));
this.$.thumbUrl = `url(${thumbUrl})`;
}
});

Expand Down Expand Up @@ -259,6 +264,7 @@ export class FileItem extends Block {
let fileInfo = await uploadFile(this.file || this.externalUrl, {
...storeSetting,
publicKey: this.$['*--cfg-pubkey'],
baseCDN: this.$['*--cfg-cdn-cname'],
userAgent: customUserAgent,
fileName: this.entry.getValue('fileName'),
onProgress: (progress) => {
Expand Down
4 changes: 3 additions & 1 deletion blocks/Img/ImgBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BaseComponent } from '../../submodules/symbiote/core/symbiote.js';
import { createCdnUrl, createCdnUrlModifiers, createOriginalUrl } from '../../utils/cdn-utils.js';
import { PROPS_MAP } from './props-map.js';

// TODO: move default config values somewhere outside
const DEFAULT_CDN_BASE = 'https://ucarecdn.com';
const CSS_PREF = '--uc-img-';
const UNRESOLVED_ATTR = 'unresolved';
const HI_RES_K = 2;
Expand Down Expand Up @@ -95,7 +97,7 @@ export class ImgBase extends BaseComponent {
if (this.$$('uuid')) {
return createCdnUrl(
//
createOriginalUrl('https://ucarecdn.com/', this.$$('uuid')),
createOriginalUrl(this.$$['cdn-cname'] || DEFAULT_CDN_BASE, this.$$('uuid')),
cdnModifiers
);
}
Expand Down
3 changes: 2 additions & 1 deletion blocks/UploadDetails/UploadDetails.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Block } from '../../abstract/Block.js';
import { createOriginalUrl } from '../../utils/cdn-utils.js';
import { fileCssBg } from '../svg-backgrounds/svg-backgrounds.js';

/**
Expand Down Expand Up @@ -124,7 +125,7 @@ export class UploadDetails extends Block {
if (uuid) {
this.eCanvas.clear();
this.set$({
cdnUrl: `https://ucarecdn.com/${uuid}/`,
cdnUrl: createOriginalUrl(this.$['*--cfg-cdn-cname'], uuid),
cloudEditBtnHidden: !this.entry.getValue('isImage') || !this.$['*--cfg-use-cloud-image-editor'],
});
this.entry.getValue('isImage') && this.eCanvas.setImageUrl(this.$.cdnUrl);
Expand Down
1 change: 1 addition & 0 deletions blocks/themes/uc-basic/config.css
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,5 @@
--cfg-data-output-form-value: 1;

--cfg-remote-tab-session-key: '';
--cfg-cdn-cname: 'https://ucarecdn.com';
}
5 changes: 3 additions & 2 deletions css-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* @typedef {{
* '*--cfg-pubkey': String;
* '*--cfg-multiple': Number;
* '*--cfg-multiple-min': Number;
* '*--cfg-multiple-max': Number;
* '*--cfg-confirm-upload': Number;
* '*--cfg-img-only': Number;
* '*--cfg-accept': String;
Expand All @@ -13,8 +15,6 @@
* '*--cfg-show-empty-list': Number;
* '*--cfg-use-local-image-editor': Number;
* '*--cfg-use-cloud-image-editor': Number;
* '*--cfg-multiple-min': Number;
* '*--cfg-multiple-max': Number;
* '*--cfg-modal-scroll-lock': Number;
* '*--cfg-modal-backdrop-strokes': Number;
* '*--cfg-source-list-wrap': Number;
Expand All @@ -25,6 +25,7 @@
* '*--cfg-data-output-from': String;
* '*--cfg-data-output-form-value': Number;
* '*--cfg-remote-tab-session-key': String;
* '*--cfg-cdn-cname': String;
* }} CssConfigTypes
*/
export {};
37 changes: 19 additions & 18 deletions docs/configuration/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,25 @@ Any configuration value can be defined and redefined at any level of the DOM tre

## Parameters description

| Name | Description | Values | Default |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | :--------------------: | :-----: |
| `--cfg-pubkey` | Your project Public Key | `'demopublickey'` | none |
| `--cfg-multiple` | Allow to upload multiple files | `1` or `0` | `1` |
| `--cfg-multiple-min` | Minimum number of files that can be selected. | number | none |
| `--cfg-multiple-max` | Maximum number of files that can be selected. | number | none |
| `--cfg-confirm-upload` | Enables user confirmation for upload starting | `1` or `0` | `1` |
| `--cfg-img-only` | Accept images only | `1` or `0` | `0` |
| `--cfg-accept` | Native file input accept attribute value | `'image/*'` | none |
| `--cfg-store` | Store files | `1` or `0` | - |
| `--cfg-camera-mirror` | Flip camera image | `1` or `0` | `0` |
| `--cfg-source-list` | Comma-separated list of file sources | `'local, url, camera'` | none |
| `--cfg-max-local-file-size-bytes` | Maximum file size in bytes | - | none |
| `--cfg-thumb-size` | Image thumbnail size | `76` | `76` |
| `--cfg-show-empty-list` | Show uploads list when it's empty | `1` or `0` | `0` |
| `--cfg-use-local-image-editor` | Enable local image editing | `1` or `0` | `0` |
| `--cfg-use-cloud-image-editor` | Enable cloud image editing | `1` or `0` | `0` |
| `--cfg-remote-tab-session-key` | Key to revoke Custom OAuth access. See [docs](https://uploadcare.com/docs/start/settings/#project-settings-advanced-oauth) for details | string | none |
| Name | Description | Values | Default |
| --------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | :--------------------: | :--------------------: |
| `--cfg-pubkey` | Your project Public Key | `'demopublickey'` | none |
| `--cfg-multiple` | Allow to upload multiple files | `1` or `0` | `1` |
| `--cfg-multiple-min` | Minimum number of files that can be selected. | number | none |
| `--cfg-multiple-max` | Maximum number of files that can be selected. | number | none |
| `--cfg-confirm-upload` | Enables user confirmation for upload starting | `1` or `0` | `1` |
| `--cfg-img-only` | Accept images only | `1` or `0` | `0` |
| `--cfg-accept` | Native file input accept attribute value | `'image/*'` | none |
| `--cfg-store` | Store files | `1` or `0` | - |
| `--cfg-camera-mirror` | Flip camera image | `1` or `0` | `0` |
| `--cfg-source-list` | Comma-separated list of file sources | `'local, url, camera'` | none |
| `--cfg-max-local-file-size-bytes` | Maximum file size in bytes | - | none |
| `--cfg-thumb-size` | Image thumbnail size | `76` | `76` |
| `--cfg-show-empty-list` | Show uploads list when it's empty | `1` or `0` | `0` |
| `--cfg-use-local-image-editor` | Enable local image editing | `1` or `0` | `0` |
| `--cfg-use-cloud-image-editor` | Enable cloud image editing | `1` or `0` | `0` |
| `--cfg-remote-tab-session-key` | Key to revoke Custom OAuth access. See [docs](https://uploadcare.com/docs/start/settings/#project-settings-advanced-oauth) for details | string | none |
| `--cfg-cdn-cname` | Set Custom CNAME. See [docs](https://uploadcare.com/docs/delivery/cdn/#custom-cdn-cname) for details | string | `https://ucarecdn.com` |

## Possible values for the source list

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"lint:css:fix": "stylelint './**/*.css' --fix",
"lint": "run-s lint:js lint:css",
"clean:web": "rimraf './web/**/*.{js,css}'",
"clean:types": "rimraf './{abstract,blocks,solutions,web,utils,submodules/symbiote}/**/*.{d.ts,d.ts.map}' && rimraf './{!global}.{d.ts,d.ts.map}'",
"clean:types": "rimraf './{abstract,blocks,solutions,web,utils,submodules/symbiote}/**/*.{d.ts,d.ts.map}' && rimraf './!(global).{d.ts,d.ts.map}'",
"clean": "run-s clean:*",
"format:js": "prettier --write './**/*.{js,cjs}'",
"format:css": "prettier --write --parser css './**/*.css'",
Expand Down
4 changes: 4 additions & 0 deletions solutions/cloud-image-editor/config.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:where(.uc-cldtr-common),
:host {
--cfg-cdn-cname: 'https://ucarecdn.com';
}
1 change: 1 addition & 0 deletions solutions/cloud-image-editor/index.css
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
@import url('./config.css');
@import url('../../blocks/CloudImageEditor/src/css/index.css');
2 changes: 1 addition & 1 deletion submodules/symbiote
Submodule symbiote updated 0 files
108 changes: 93 additions & 15 deletions utils/cdn-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/**
* Trim leading `-/`, `/` and trailing `/` from CDN operation
*
* @param {String | unknown} [operation]
* @returns {String}
*/
Expand All @@ -20,7 +22,9 @@ export const normalizeCdnOperation = (operation) => {
};

/**
* @param {...(String | unknown)} operations
* Join multiple CDN operations into one string without trailing or leading delimeters
*
* @param {...(String | unknown)} [operations]
* @returns {String}
*/
export const joinCdnOperations = (...operations) => {
Expand All @@ -31,7 +35,10 @@ export const joinCdnOperations = (...operations) => {
};

/**
* @param {...(String | unknown)} [cdnOperations]
* Create string with leading `-/` from passed CDN operations. Do the same as `joinCdnOperations` but adds leading `-/`
* and trailing `/`
*
* @param {...(String | unknown)} [cdnOperations] -
* @returns {String}
*/
export const createCdnUrlModifiers = (...cdnOperations) => {
Expand All @@ -40,31 +47,102 @@ export const createCdnUrlModifiers = (...cdnOperations) => {
};

/**
* @param {String} input
* Extract filename or file URL
*
* @param {String} cdnUrl
* @returns {String}
*/
function withTrailingSlash(input) {
if (!input.endsWith('/')) {
input = input + '/';
export function extractFilename(cdnUrl) {
let url = new URL(cdnUrl);
let noOrigin = url.pathname + url.search + url.hash;
let urlFilenameIdx = noOrigin.lastIndexOf('http');
let plainFilenameIdx = noOrigin.lastIndexOf('/');
let filename = '';

if (urlFilenameIdx >= 0) {
filename = noOrigin.slice(urlFilenameIdx);
} else if (plainFilenameIdx >= 0) {
filename = noOrigin.slice(plainFilenameIdx + 1);
}
return input;

return filename;
}

/**
* @param {String} baseUrl
* @param {String} [cdnModifiers]
* @param {String} [filename]
* Trim filename or file URL
*
* @param {String} cdnUrl
* @returns {String}
*/
export const createCdnUrl = (baseUrl, cdnModifiers, filename) => {
return withTrailingSlash(baseUrl) + (cdnModifiers || '') + (filename || '');
export function trimFilename(cdnUrl) {
let url = new URL(cdnUrl);
let filename = extractFilename(cdnUrl);
let filenamePathPart = isFileUrl(filename) ? splitFileUrl(filename).pathname : filename;

url.pathname = url.pathname.replace(filenamePathPart, '');
url.search = '';
url.hash = '';
return url.toString();
}

/**
* Detect if filename is actually file URL
*
* @param {String} filename
* @returns {Boolean}
*/
export function isFileUrl(filename) {
return filename.startsWith('http');
}

/**
* Split file URL into the path and search parts
*
* @param {String} fileUrl
* @returns {{ pathname: String; search: String; hash: String }}
*/
export function splitFileUrl(fileUrl) {
let url = new URL(fileUrl);
return {
pathname: url.origin + url.pathname || '',
search: url.search || '',
hash: url.hash || '',
};
}

/**
* Create a final CDN URL with CDN modifiers and filename
*
* @param {String} baseCdnUrl - Base URL to CDN or Proxy, CDN modifiers and filename accepted
* @param {String} [cdnModifiers] - CDN modifiers to apply, will be appended to `baseCdnUrl` ones
* @param {String} [filename] - Filename for CDN or file URL for Proxy, will override one from `baseCdnUrl`
* @returns {String}
*/
export const createCdnUrl = (baseCdnUrl, cdnModifiers, filename) => {
let url = new URL(trimFilename(baseCdnUrl));
filename = filename || extractFilename(baseCdnUrl);

if (isFileUrl(filename)) {
let splitted = splitFileUrl(filename);
url.pathname = url.pathname + (cdnModifiers || '') + (splitted.pathname || '');
url.search = splitted.search;
url.hash = splitted.hash;
} else {
url.pathname = url.pathname + (cdnModifiers || '') + (filename || '');
}

return url.toString();
};

/**
* @param {String} cdnBase
* Create URL for an original file on CDN
*
* @param {String} cdnUrl - URL to get base domain from, any pathname will be stripped
* @param {String} uuid
* @returns {String}
*/
export const createOriginalUrl = (cdnBase, uuid) => {
return withTrailingSlash(cdnBase) + uuid + '/';
export const createOriginalUrl = (cdnUrl, uuid) => {
let url = new URL(cdnUrl);
url.pathname = uuid + '/';
return url.toString();
};
Loading

0 comments on commit 3d6c677

Please sign in to comment.