Code Monkey home page Code Monkey logo

imgup's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

imgup's Issues

Chore: Compressed landscape (aspect) images add black border

At present the compressed images work fine until we have a wider image ...
https://jsfiddle.net/6t7p0eqc/7/

dwyl-imgup-compressed-images-add-black-border

Original

image

Compressed:

image

Todo

.resize({
width: 200,
height: 200,
fit: 'contain'
})

  • Should only constrain in one dimension e.g: only width or only height
    i.e. preserve the aspect ratio of images.

  • Redeploy the compressor app on AWS.

  • Confirm that compressed images are preserving aspect ratio (not adding black border)

Server side image uploads

We are trying to implement an image upload from our server.
We need to replicate the form submit from the direct-upload example on the back end but we're having trouble structuring our http POST request.
The form data that we want to send are the params returned from our getS3Credentials function.
The url of the form is the endpoint url from the same function.

Our form looks like this:

<form>
  <input id="fileInput" type="file" name="file" onchange="uploadDemo.saveFile(this.files)"/>
</form>
<button id="submit" onclick="uploadDemo.submitFile()">Submit</button>

And this is what we're dynamically adding before we submit the form:

function buildAndSubmitForm (s3Data) {
    var form = document.querySelector('form')
    var keyInput = document.createElement('input')
    keyInput.setAttribute('type', 'hidden')
    keyInput.setAttribute('name', 'key')
    keyInput.setAttribute('value', s3Data.filename)
    form.setAttribute('method', 'post')
    form.setAttribute('action', s3Data.endpoint_url)
    form.setAttribute('enctype', 'multipart/form-data')
    form.insertBefore(keyInput, form.firstChild)
    form.url = s3Data.endpoint_url
    form.formData = s3Data.params
    form.submit()
  }

Does anyone know how our POST request should look in order for our form data to be sent in the same fashion as our front end example?

Should we Upload images to Google Drive or Google Photos?

With most cloud storage services there is a cost associated with storing photos.
Granted, the cost is low e.g $0.03 per GB (per month) on AWS S3 ... but it can add up if you have many photos e.g. 100GB is $36 per year and that would only increase over time...

So, if instead of using AWS S3 we use Google Drive or Google Photos we get 15 GB "Free":
drive-storage

or "Unlimited Free" (Google Photos):
google-photos-unlimited

See:

@iteles thoughts...?

`Who` can/should use our image uploading + resizing service?

The "demo" version of the imgup App will be open to allow people to test it.
We will not DELETE the files unless the AWS S3 bill gets out of hand e.g: bots start uploading.
I don't expect nefarious people to discover/use the imgup App ...
but if they do we will rapidly respond and put all uploading behind Auth.
The REST API will have Auth ASAP because that is where abuse is most likely to originate.
See:

But we need to enable logging: dwyl/logs#1
so that we can:
A. See who is trying the demo and from where.

Epic: Upload images to `AWS S3` via `Phoenix LiveView`

At present this repo is just a loose collection of notes and some sample code.
What it should be instead is an end-to-end working example of uploading, compressing, resizing, rotating and cropping images.

Goal

Our goal with image uploads is to be able to seamlessly upload any type of image
have it saved in a reliable place e.g. S3 (though we can research cheaper comparable options later...)
and display that image in our App and other places.

We want to be able to upload in a Web interface and in a Flutter App.

Todo

  • Move all existing code to an _archive directory in case we need to refer to any of it.
    • Move the README.md to _archive too so you have a fresh start.

    Note: it will all be DELETED once we have fully working version.

  • Watch Chris McCord's LiveView Uploads Demo:

Phoenix LiveView Uploads Deep Dive: https://youtu.be/PffpT2eslH8
image

If we can get to the point where we are uploading images in a Phoenix LiveView App we will already be showing great progress.
Don't worry too much about the UI/UX at this stage. Use TailwindUI:
https://tailwindui.com/components/application-ui/forms/form-layouts
image

@LuchoTurtle as discussed on standup this morning, this should be the next "big chunk" of work you should attempt to tackle.
If you have questions, please comment. πŸ™

Updating the SDK example: how much public access should be granted?

I am getting this warning whilst following the SDK example:

image

I chose the following access permissions:
image

Based on the guidance from this step of the existing tutorial:

image

I wanted to get a second opinion on whether I had interpreted the updated version of the steps correctly and therefore whether I should react to this warning message?

Rotate images after they have been uploaded

When uploading an image from a mobile device, the image is often shot in landscape or "upside-down" mode (e.g. selfies). Users are begging us for a way of rotating images so they appear the right way up.
Example UI

Edge cases in Upload

I've spotted some possible errors with the following code:

imgup/lib/app/upload.ex

Lines 23 to 28 in 81de078

def upload(image) do
# Create `CID` from file contents so filenames are unique
#
{:ok, file_binary} = File.read(image.path)
file_cid = Cid.cid(file_binary)
file_name = "#{file_cid}.#{Enum.at(MIME.extensions(image.content_type), 0)}"

Not an error in itself but I think we can make this a bit more readable:

file_extension = image.content_type
|> MIME.extensions()
|> List.first()

file_name = "#{file_cid}.#{file_extension}" 

Also MIME.extension can return an empty list, so in this case the file extension would be nil. Do you still want to upload the file if the extension is unknown? If yes maybe the file name should be just the file_cid
see https://hexdocs.pm/mime/MIME.html#extensions/1-examples
image

Feat: Filenames on `S3` should be the `cid` of the content ...

At present Unique File Names are using DateTime.utc_now():

image

This is does not take advantage of cid.
The idea of cid is that when a file is uploaded the name of the file is based on the actual content of the file ...
Therefore we need to read the binary of the file and feed that into cid/1 such that when the same file is uploaded again, it will have the same filename on S3.
The reason we want to do this is simple: we avoid duplicates.

Todo

  • update the function key variable to use a cid of the actual contents of the file.

Task List

  • Current example uses blueimp and jquery to perform the request to S3 - re-write in vanilla XHR
  • Convert the server from express to hapi
  • Add tests for server
  • Return bucket name as an environment variable from the server
  • Add end-to-end tests (nightwatch) #15

chore: Have bucket names imported env variables

As discussed in #70 (review), the buckets should be env variables because they are used across the application to upload files, retrieve the URLs of the latter, and show them to the person using the app/API.

Because the bucket names are implicitly used in these URLs and when creating the needed metadata to upload files from the LiveView, we ought to have them config'd and use them from there.

What is the best way to allow for multiple image uploads?

What is the best way to allow for multiple image uploads?

For an e-commerce site where a product can has one primary photo plus multiple secondary photos, what is the best way to enable uploading multiple images?

  1. If you upload multiple images one at a time just like a single upload app, what naming conventions should you follow in order to store the images linked to a particular product? E.g. product a1b has images a1b-1, a1b-2 etc. Is using an end number useful? Could this number go into double or triple digits if necessary?
  2. How can you enable users to upload multiple images in one go? E.g.
    image
  3. How can you differentiate between the primary and secondary images? How do you update the primary image?

Question 1 is of the highest priority for me right now but I will also want to consider the other questions in due course.

[error] ** (UndefinedFunctionError) function :hackney.request/5 is undefined (module :hackney is not available)

The REST API PR #61 was merged. βœ…
But sadly when we attempt to upload an image via Hoppscotch we get the following error:
https://imgup.fly.dev/api/images

image

Logs: https://fly.io/apps/imgup/monitoring

imgup-hackney-request-fails
[error]  ** (UndefinedFunctionError) function :hackney.request/5 is undefined (module :hackney is not available)

[info] :hackney.request(:post, "https://s3.eu-west-3.amazonaws.com/imgup-original/zb2rhg1D3wGz3ps1wsUURdkSPLeFG5NTihkMtnpHHnXnFvSSo.png?uploads=1", 
[{"Authorization", "AWS4-HMAC-SHA256 
Credential=AKIAETC/20230614/eu-west-3/s3/aws4_request,SignedHeaders=content-length;host;x-amz-acl;x-amz-content-sha256;x-amz-date,Signature=65011ed0fc91075d3d429cda2ab70eda47ae4d3b214f187b34dbb0e8de8244a4"}, 
{"host", "s3.eu-west-3.amazonaws.com"}, {"x-amz-date", "20230614T102209Z"}, 
{"content-length", "0"}, {"x-amz-acl", "public-read"}, 
{"x-amz-content-sha256", "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"}], 
"", [:with_body, {:recv_timeout, 30000}])

Very curious why the {"content-length", "0"} is it not reading the file? πŸ’­

@LuchoTurtle do you mind investigating this? πŸ™
Feel free to:
a) revert back to your version of the API from before I made changes.
b) deploy directly to Fly to figure out why the uploads don't work.

How to use this repo without a node backend?

I want to use this repo for an elixir project and saw the section about setting up a node server and using node packages crypto and path. I don't think it's good practice to have the two running simultaneously.

We don't have the time on this occasion to rewrite this repo entirely in elixir and so I wanted to know if there is another alternative?

New Version!

The time has come to update (revive) this project and bring it into 2016!

Β Steps:

  • git mv README.md REF.md (temporarily keep a reference to the old README.md so we can pluck sections from it e.g. Research on Orientation and other uploaders etc.)
  • Create a brand new readme with a new problem statement (this can be inspired by the old one but needs to be fresh)

Next we need to Remove all references to Meteor (people writing meteor apps will still be able to use this project, but we need to break the tight-coupling)

  • git rm -r .meteor
  • git rm -r client
  • git rm -r packages
  • git rm -r private
  • git rm -r public
  • git mv server lib
  • git mv lib/server_save_file.js lib/index.js (we will remove most of the code in this file shortly but it gives us some history #nostalgia ...)
  • git rm npm-debug.log
  • git mv packages.json package.json (convert from Meteor project to standard node.js project)
  • git rm npm-debug.log
  • Update the .gitignore with the relevant definitions from https://github.com/github/gitignore/blob/master/Node.gitignore (_we only need to ignore node_modules, npm-debug.log* and coverage for now...
  • git rm smart.json
  • git rm smart.lock
  • git rm style
  • git rm style.css

Hi @jackcarlisle please create a pull request with the above changes and call it "reset" and assign to me. Then we can start working on the actual features... thanks! πŸ‘

Bug: Cannot include `S3` image in a `GitHub` issue comment

We are able to upload images to S3 via REST API πŸŽ‰
URL: https://imgup.fly.dev/api/images
Docs: https://github.com/dwyl/imgup/blob/main/api.md#4-testing-the-api-from-hoppscotch

image
{
  "compressed_url": "https://s3.eu-west-3.amazonaws.com/imgup-compressed/zb2rhoFgi1PXSfoeFi9PGT3R33fpq4iAsQJRuU2Yoy7yMzhj3.jpg",
  "url": "https://s3.eu-west-3.amazonaws.com/imgup-original/zb2rhoFgi1PXSfoeFi9PGT3R33fpq4iAsQJRuU2Yoy7yMzhj3.jpg"
}

The problem is when we attempt to view one of these images directly in the browser e.g. by copy-pasting the URL into a new browser tab: https://s3.eu-west-3.amazonaws.com/imgup-original/zb2rhoFgi1PXSfoeFi9PGT3R33fpq4iAsQJRuU2Yoy7yMzhj3.jpg

It automatically downloads the file instead of displaying it in the browser.
I suspect this is because the MIME Type is not correctly set on S3 ...

This is why it does not display the image in a GitHub issue comment or .md file ...

![img](https://s3.eu-west-3.amazonaws.com/imgup-original/zb2rhoFgi1PXSfoeFi9PGT3R33fpq4iAsQJRuU2Yoy7yMzhj3.jpg)

img

... πŸ€·β€β™‚οΈ

@LuchoTurtle do you have time to investigate this?
I've tried reading the docs: https://hexdocs.pm/ex_aws_s3/ExAws.S3.html#t:initiate_multipart_upload_opt/0
but can't seem to find where to put the Content-Type for the uploaded file ... πŸ€·β€β™‚οΈ

** (System.EnvError) could not fetch environment variable "AWS_ACCESS_KEY_ID" because it is not set

https://github.com/dwyl/imgup/actions/runs/5261384190/jobs/9509426771#step:4:26
image

/home/runner/work/_temp/.setup-beam/elixir/bin/mix local.rebar --force
  ** (System.EnvError) could not fetch environment variable "AWS_ACCESS_KEY_ID" because it is not set
      (elixir 1.14.2) lib/system.ex:706: System.fetch_env!/1
      /home/runner/work/imgup/imgup/config/config.exs:54: (file)
      (stdlib 4.1.1) erl_eval.erl:748: :erl_eval.do_apply/7
      (stdlib 4.1.1) erl_eval.erl:961: :erl_eval.expr_list/7
      (stdlib 4.1.1) erl_eval.erl:290: :erl_eval.expr/6
      (stdlib 4.1.1) erl_eval.erl:[28](https://github.com/dwyl/imgup/actions/runs/5261384190/jobs/9509426771#step:4:31)2: :erl_eval.expr/6
      (stdlib 4.1.1) erl_eval.erl:961: :erl_eval.expr_list/7
  Error: The process '/home/runner/work/_temp/.setup-beam/elixir/bin/mix' failed with exit code 1

Tried to add: https://github.com/dwyl/imgup/settings/secrets/actions but got the following error:
image

Failed to add secret, a secret with the same name (AWS_ACCESS_KEY_ID) already exists.

What specs should we use for product images for a mobile centric e-commerce site?

I am working on a mobile-first project that is using image uploads for product images. In this instance the products are drinks (bottles). The client has asked what specs they should follow for providing the images for these products. We want to ensure images are of a good enough quality but that they also load quickly. So I want to know what specs I should request?

API Upload fails on Fly.dev "Required key: :secret_access_key is nil in config!"

The app is deployed to Fly: https://imgup.fly.dev/
image

But when we attempt to upload via the REST API in Hoppscotch we see the following error:
image

{
  "errors": {
    "detail": "Error uploading file #26"
  }
}

Viewing the monitoring: https://fly.io/apps/imgup/monitoring we see the following error in the logs:
image

** (MatchError) no match of right hand side value: {:error, "Required key: :secret_access_key is nil in config!"}

Just updated the Secrets: https://fly.io/apps/imgup/secrets

image

Still getting the same issue. Works on localhost ... πŸ€·β€β™‚οΈ

Going to investigate this now as the code is still fresh in my head. πŸ”

Feat: Logging + Metrics

This is "on hold" ... pending dwyl/logs#1

At present the imgup app has no DB πŸ’­
So we have zero insight into what images are being uploaded. πŸ™ˆ πŸ€·β€β™‚οΈ
This is "OK" for a basic demo... :shipit:
But it's not great for a "production" app where we want to track metrics. πŸ“ˆ
We need to "productionize"! πŸš€

Todo

  • Define the DB schema for storing anon logging data.
    We can borrow (or draw inspiration) from: dwyl/auth/lib/auth/log.ex
    • user_agent table should mimmic the one in auth: lib/auth/user_agent.ex so that we store the relevant detail. The other field we could add is
  • Create the necessary migrations for the schema we define.
  • Create a simple interface that logs required events ...

attempting install

W20150221-18:55:05.788(-8)? (STDERR) ReferenceError: require is not defined
W20150221-18:55:05.788(-8)? (STDERR) at app/node_modules/imagemagick/imagemagick.js:1:52
W20150221-18:55:05.789(-8)? (STDERR) at app/node_modules/imagemagick/imagemagick.js:417:3

fontawesome

looks like old package is no longer available, thus project does not work after cloning git repo

Chore: Remove Image Expiry (Don't delete images from S3)

At present there is a policy to automatically DELETE images after a period of time.
While this is useful for some projects, it's not a requirement for us.

Todo

  • Please remove this auto-delete policy so that images are kept.

Note: we can easily manually delete images if needed.

BUG: Couldn't upload files to S3. Open an issue on Github and contact the repo owner.

Getting the following error when attempting to upload on: https://imgup.fly.dev/liveview

Couldn't upload files to S3. Open an issue on Github and contact the repo owner.
image

@LuchoTurtle can you please investigate why if/when you have time? Thanks. πŸ™

Note: this is almost certainly related to the work done for the "Edge Cases" PR: #86 / #69
The tests all pass on that branch so either the LiveView upload is insufficiently tested or the tests are a false positive. πŸ’­

Feat: Image Upload API

Once we have the basic Web-based image uploading working there are many enhancements we can make. ✨
Hopefully by making everything Open Source - as always - we invite contributions from the community. 🀞
However from our perspective @dwyl what we want is the ability to upload from our Flutter (Native Mobile) App.

Todo

  • Create a Secure REST API endpoint that allows a client e.g. JS or Flutter to upload an image
  • Ideally should be streaming to provide visual feedback of the upload progress
  • Should return the URL of the image once uploaded

Note: this issue is not complete. It's a place-holder for the discussion around features/requirements.
Once we have #51 working in the Web interface, this is the next logical step.

Testing using the sdk-upload using Hapi's server.inject

We are not using nightwatch in our project at the moment, but the tests in the sdk-upload example are only done using nightwatch. It would be really helpful to see another way to test this upload method.

We are using Hapi's server.inject method and after a lot of playing around, eventually managed to upload a file; we only managed this with a .txt file. There are a couple of working examples linked at the bottom which may hold the solution.

Here is a link to the PR on my project that contains the image upload with a test that successfully uploads a file to our S3 bucket using server.inject.

I thought it would be useful to copy our method into an issue here, and we can develop it as and when we work out a better/more comprehensive way of testing

// our form has the inputs: name, file_name, mission_statement, logo
// logo represents the file, and file_name is the name of the file being uploaded
// name and mission statement are additional fields that our form needs to track
// at the moment, we are struggling to upload an image, so the logo is actually a .txt file
var payloadString = 
  '--AaB03x\r\n' +
  'content-disposition: form-data; name="name"\r\n' + // input on our form called `name`
  '\r\n' +
  'Apple\r\n' + // The value of the input `name` is "Apple"
  '--AaB03x\r\n' +
  'content-disposition: form-data; name="file_name"\r\n' + // input on our form called `file_name`
  '\r\n' +
  'foxy.txt\r\n' +  // The value of the input `file_name` is "foxy.txt"
  '--AaB03x\r\n' +
  'content-disposition: form-data; name="mission_statement"\r\n' + // input on our form called `mission_statement`
  '\r\n' +
  'Change the economy!\r\n' + // The value of the input `mission_statement` is "Change the economy!"
  '--AaB03x\r\n' +
  'content-disposition: form-data; name="logo"; filename="foxy.txt"\r\n' + // input on our form called `logo`
  'Content-Type: text/plain\r\n' + // content type of the file being sent in the form
  '\r\n' +
  'foxxxxyy\r\r\n' +  // contents of the file being sent in the form
  '--AaB03x--\r\n'


var addLogoOptions = {
    method: 'POST',
    url: '/add-image',
    headers: {
      'Content-Type': 'multipart/form-data; boundary=AaB03x' // notice this boundary repeated before/after each input in the payload string
    },
    payload: payloadString
  };
}


// the tape test
tape('/add-image route successfully adds an image to S3 --> ' + __filename, function (t) {
  server.inject(addLogoOptions, function (res) {
    t.equal(res.statusCode, 200, 'status code is 200 we can upload a file to S3');
    t.end();
  });
});

This test passes, the payload string was taken and adapted from this issue on the hapi repo

It looks like there might be a slightly simpler way of testing the file upload in this issue

Finally this is a working example of the potentially more simple way of solving this problem

How to test S3 image uploading on your project?

How should we test the image uploading functionality on a project?

I can see this nightwatch example on this repo:
https://github.com/dwyl/image-uploads/blob/master/examples/sdk-upload/test/e2e/sdk-upload.test.js

However my concern is that every time you perform this test you are uploading a new file to S3. Unless someone in a team remembers to delete the test files on a regular basis you could end up with a lot of files. This comes as a cost on S3 if it was a large amount of data being stored.

Would it be possible/better to delete the files once they're uploaded? Or can we mock the upload in some way but still test it?

Example/Showcase of Image Upload UX/UI?

Image uploading is a common feature in many web/mobile applications.

We are going to be using the Image Upload feature extensively in Time
to allow people to upload a photo showing evidence of their completed activity.
(if you cannot visualise the use-case of this, please comment in: dwyl/app#215)

Additionally there are several classes of applications where image uploading is relevant:

  • Dedicated Photo Sharing App (the obvious one)
  • Messaging/Chat (where "a picture is worth a thousand words") Look no further than SnapChat!
  • Personal/Company Finance & Accounting (Uploading Receipts)

By building a clearly-explained Image Upload example/tutorial with re-useable code,
we can help others get creative and spawn new Apps/Projects in the community.

Todo

  • Create an start-to-finish Image Upload Example
  • Deploy it to Heroku
  • Make the code generic and modular so that anyone can use it in their project.

Questions we need to ask/answer:

  • How do we secure our /upload endpoint to avoid people using it maliciously to upload undesirable images.
    • Can we restrict the Origin of the upload?
      • Can the Origin be spoofed by a command-line script?

I'm not saying that we are going to create an "image upload" product,
But the ability to upload files/images is a "standalone" feature that people pay for!
e.g: https://apps.shopify.com/uploadery
This is a feature that has real value to end-users so if we build something great
not only will the people using the @dwyl app find it useful,
the wider community will use it and help us to improve it!

S3 bucket access denied

I'm having issues with the config.json file I think (credentials or permissions).

This is the error I'm getting on the frontend side:

image

This one on Meteor console:

image

If I try loading the link that gave an error on the frontend I get this:

ImΓ‘genes integradas 3
image

S3 account:

image

config.json file on the App:

image

I gave a bucket policy for testingboris since I was getting denied error but didn't do the trick:

image

image

It seems to be a permission issue with the bucket.

Another thing I had to do with the project was to change require() to Meteor.require if not I got an error on console. (require is not defined).

Really Appreciate some help on this!

`imgup` Fly.io App deployment overwriting AWS env vars leading to failure

At present the automatic continuous deployment (CD) from GitHub actions to Fly.io for the imgup app works partially.

https://imgup.fly.dev/liveview

imgup-liveview-upload-working.mov

However the REST API fails when we attempt to upload in Hoppscotch:
image

The app was recently deployed: https://fly.io/apps/imgup
image

In the logs following the failed API request we see the following error:
https://fly.io/apps/imgup/monitoring
image

[error] ** (MatchError) no match of right hand side value: {:error, "Required key: :secret_access_key is nil in config!"}

Which relates to the following line of code:

Logger.error(Exception.format(:error, e, __STACKTRACE__))

Not super informative, but we know the upload is failing due to unavailability of the AWS :secret_access_key ... πŸ™ƒ

So we know what needs to be fixed ... 🀞

Todo

  • Remove the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from ci.yml:
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
  • Update the FLY_API_TOKEN to the dwyl account.
  • Create PR updating.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.