Giter VIP home page Giter VIP logo

filite's Introduction

filite

THIS PROJECT IS CURRENTLY UNMAINTAINED AS I NO LONGER USE IT MYSELF AND DO NOT HAVE THE TIME NOR ENERGY TO COMMIT TO MAINTAINING IT. SORRY.

The master branch isn't actively maintained anymore, and the current development branch, next, will be merged into it in the near future.

A simple, light and standalone pastebin, URL shortener and file-sharing service that hosts files, redirects links and stores texts.

GitHub Actions Crates.io

Live Example (file upload disabled and rate limited)

Table of Contents

Features

What it is

  • Easy to use. Installation and set-up take less than a minute and a built-in web UI is provided.
  • Standalone. No external dependencies required, everything that is needed is packed into the binary.
  • Light and fast. The Rust web framework Actix is used under the hood, providing great speed with a minimal footprint.

What it is not

  • A tracking tool. No stats are stored to increase speed, reduce resource usage and maintain simplicity, if this is what you are looking for filite is not for you.

Installation

  1. Get the binary either from the releases page or using Cargo
  2. Run filite init to perform the initial setup (you can do this at any time to reset the config and password)
  3. Edit your config file as you see fit (check the dedicated section for details)
  4. Run filite

That's it!

Usage

When asked for a login, use whatever username you want and the password you provided during setup. Details for programmatic usage are provided in the dedicated section.

Planned features

  • Decent test suite
  • TLS support
  • Simple admin page
  • Multiple logins (?)

Config

# Port to listen on
port = 8080
# SQLite database connection url
database_url = "database.db"
# SQLite database connection pool size
pool_size = 4
# Directory where to store static files
files_dir = "files"

# Highlight.js configuration
[highlight]
# Theme to use
theme = "github"
# Additional languages to include
languages = ["rust"]

Client tools

ShareX

  • <AUTHORIZATION> is the result of encoding <USERNAME>:<PASSWORD> to base64
    • <USERNAME> is an arbitrary username, it doesn't matter
    • <PASSWORD> is the password entered during setup
  • <ADDRESS> is the root address where the filite is running, for instance http://localhost:8080 or https://filite.raphaeltheriault.com

File

{
  "Version": "13.0.1",
  "Name": "filite (file)",
  "DestinationType": "ImageUploader, FileUploader",
  "RequestMethod": "POST",
  "RequestURL": "<ADDRESS>/f",
  "Headers": {
    "Authorization": "Basic <AUTORIZATION>"
  },
  "Body": "MultipartFormData",
  "FileFormName": "file",
  "URL": "<ADDRESS>/$response$"
}

Link

{
  "Version": "13.0.1",
  "Name": "filite (link)",
  "DestinationType": "URLShortener",
  "RequestMethod": "POST",
  "RequestURL": "<ADDRESS>/l",
  "Headers": {
    "Authorization": "Basic <AUTORIZATION>"
  },
  "Body": "JSON",
  "Data": "{\"forward\":\"$input$\"}",
  "URL": "<ADDRESS>/l/$response$"
}

Text

You can remove the prompt and always enable or disable syntax highlighting by replacing $prompt:Highlight|false$ with true or false.

{
  "Version": "13.0.1",
  "Name": "filite (text)",
  "DestinationType": "TextUploader",
  "RequestMethod": "POST",
  "RequestURL": "<ADDRESS>/t",
  "Headers": {
    "Authorization": "Basic <AUTORIZATION>"
  },
  "Body": "JSON",
  "Data": "{\"contents\":\"$input$\",\"highlight\":$prompt:Highlight|false$}",
  "URL": "<ADDRESS>/t/$response$"
}

Reverse proxy

  • <DOMAIN> is the domain the requests will be coming from, for instance filite.raphaeltheriault.com
  • <PORT> is the port on which filite is listening

Upload limits are set to 10M as an example

NGINX

server {
  listen 80;
  listen [::]:80;

  server_name <DOMAIN>;

  location / {
    proxy_pass http://localhost:<PORT>;

    location /f {
      client_max_body_size 10M;
    }
  }
}

Apache

<VirtualHost *:80>
  ServerName <DOMAIN>

  ProxyPreserveHost On
  ProxyPass / http://localhost:<PORT>/
  ProxyPassReverse / http://localhost:<PORT>/

  <Location "/f">
    LimitRequestBody 10000000
  </Location>
</VirtualHost>

Programmatic usage

All requests that require authentication use HTTP Basic Auth (without taking the username into account).

Listing existing entries

It's possible to get an array of all existing entries for each type with an authenticated request.

  • GET /f
  • GET /l
  • GET /t

You can view the created entries in a more organized manner with the third-party filite-list script.

Creating new entries

There are two ways to create new entries, PUT or POST requests. PUT lets you choose the ID manually and POST assigns a free one automatically, but that's the only difference. Both methods require authentication.

PUT requests will overwrite any existing entry.

Files

  • PUT /f/{id}
  • POST /f

Files are sent as multipart/form-data. The field name isn't important but the file name needs to be included. Only one file is treated.

Links

  • PUT /l/{id}
  • POST /l

Links are sent as application/json according to the following schema.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Link",
  "type": "object",
  "properties": {
    "forward": {
      "description": "URL this link forwards to",
      "type": "string"
    }
  }
}

Texts

  • PUT /t/{id}
  • POST /t

Texts are sent as application/json according to the following schema.

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Text",
  "type": "object",
  "properties": {
    "contents": {
      "description": "Text contents",
      "type": "string"
    },
    "highlight": {
      "description": "Whether to enable code highlighting or not for that text",
      "type": "boolean"
    }
  }
}

Deleting entries

It's possible to delete any entry with an authenticated request.

  • DELETE /f
  • DELETE /l
  • DELETE /t

Contributing

The project is open to contributions! Before submitting a PR, make sure your changes work both with and without the dev feature enabled.

Requirements

  • The Rust toolchain
  • diesel_cli with the sqlite feature enabled

Setup

  1. Copy .env.example to .env and set the variables to your liking
  2. Run diesel database setup
  3. Build or run with the dev feature enabled

License

filite is licensed under the MIT License.

filite's People

Contributors

raftario avatar sjm42 avatar steven-joruk avatar syndamia avatar vam-jam avatar wezm avatar

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

filite's Issues

Overwriting URLs

Right now, when a user inputs a URL which is already in use, the input box has a yellow outline to express that the URL is in use. The user can go ahead with the action, and his new request overwrites the content was submitted previously using that URL.

This behavior makes sense when filite is used by an individual or in a group with common interests. But, when filite is used publicly, this poses a BIG issue. Because, anyone can hijack the URL of another person.

What's the best way to fix this issue?

  • Disable overwriting for filite (globally, via config file)
  • Checkbox to disable overwriting while creating a URL

Build for armhf processor?

I tried to execute filite init, but it failed with error:
cannot execute binary file: exec format error. My device is raspberry pi 3 with raspbian. Thank you for help.

Question: How to delete an entry?

I really like your pastebin tool!
I was trying to delete an entry using the following command:

curl -X DELETE https://username:[email protected]/t/<ID>

ID is the ID of the entry (12548) when you get the output of https://username:[email protected]/t:

[
  {
    "id": 12548,
    "contents": "This is a test!",
    "created": 1573691264,
    "highlight": false
  }
]

I get the reply Deleted but the entry is still there... Is this the wrong way to delete an entry?

[Ported] Win32 Port

I managed to port this application to a 32 bit windows application.
It took a while. But wow, it was worth it. Now i can run this on a lightweight virtual machine. Running 32 bit.

If someone else wants to download it. Just ask.. (reply).

Images:
filite-1
filite-2
filite-3
filite-4

:D

Service hangs up without any response

Installed 0.3.0 from releases.

Config

port = 8080
database_url = "/root/.local/share/filite/database.db"
pool_size = 1
files_dir = "/root/.local/share/filite/files"

[highlight]
theme = "github"
languages = ["rust"]

Upload

curl -u luna:pass -X POST -F test.z=@/usr/share/vim/vimrc localhost:8080/f

Result

thread 'actix-web' panicked at 'called `Result::unwrap()` on an `Err` value: Error(None)', src/libcore/result.rs:1165:5
thread 'actix-web' panicked at 'called `Result::unwrap()` on an `Err` value: Error(None)', src/libcore/result.rs:1165:5
stack backtrace:
   0:     0x55fa42e33364 - backtrace::backtrace::libunwind::trace::hda41dbcdfba36aa0
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/libunwind.rs:88
   1:     0x55fa42e33364 - backtrace::backtrace::trace_unsynchronized::h1a8d6e1f8cb3f5d4
                               at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.37/src/backtrace/mod.rs:66
   2:     0x55fa42e33364 - std::sys_common::backtrace::_print_fmt::h610c4127487e10da
                               at src/libstd/sys_common/backtrace.rs:76
   3:     0x55fa42e33364 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::h0722dc552e01bd1d
                               at src/libstd/sys_common/backtrace.rs:60
   4:     0x55fa42e5644c - core::fmt::write::h01edf6dd68a42c9c
                               at src/libcore/fmt/mod.rs:1030
   5:     0x55fa42e2e4c7 - std::io::Write::write_fmt::hf15985f193f03c04
                               at src/libstd/io/mod.rs:1412
   6:     0x55fa42e35895 - std::sys_common::backtrace::_print::hd8d5d08a1795e743
                               at src/libstd/sys_common/backtrace.rs:64
   7:     0x55fa42e35895 - std::sys_common::backtrace::print::hf89a79e3921a2366
                               at src/libstd/sys_common/backtrace.rs:49
   8:     0x55fa42e35895 - std::panicking::default_hook::{{closure}}::h3a8f42beb3bb8ae3
                               at src/libstd/panicking.rs:196
   9:     0x55fa42e35586 - std::panicking::default_hook::h8f803b0bc31a5c37
                               at src/libstd/panicking.rs:210
  10:     0x55fa42e35f95 - std::panicking::rust_panic_with_hook::h825f041245da8739
                               at src/libstd/panicking.rs:473
  11:     0x55fa42e35b32 - std::panicking::continue_panic_fmt::hbe0378e33481e81b
                               at src/libstd/panicking.rs:380
  12:     0x55fa42e35a26 - rust_begin_unwind
                               at src/libstd/panicking.rs:307
  13:     0x55fa42e52b4a - core::panicking::panic_fmt::h527855ce0bc891f6
                               at src/libcore/panicking.rs:85
  14:     0x55fa42e52c47 - core::result::unwrap_failed::ha8b77e6004f0ba38
                               at src/libcore/result.rs:1165
  15:     0x55fa422bd2c0 - core::result::Result<T,E>::unwrap::h21ee83cd6b2c843d
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libcore/result.rs:933
  16:     0x55fa420fac01 - filite::queries::files::find::h809f8f6254cc9b90
                               at src/queries.rs:60
  17:     0x55fa420fa477 - filite::queries::files::replace::hb4029f994f19abf4
                               at src/queries.rs:137
  18:     0x55fa4223e505 - filite::routes::files::put_post::{{closure}}::{{closure}}::h0f7f239e8387f156
                               at src/routes.rs:386
  19:     0x55fa4223857c - actix_threadpool::run::{{closure}}::{{closure}}::haa1e2b11cf78add1
                               at /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/actix-threadpool-0.3.1/src/lib.rs:63
  20:     0x55fa42239702 - <F as threadpool::FnBox>::call_box::hed80014852094c62
                               at /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/threadpool-1.7.1/src/lib.rs:95
  21:     0x55fa42a82818 - threadpool::spawn_in_pool::{{closure}}::h7e46ed507619a800
                               at /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/threadpool-1.7.1/src/lib.rs:767
  22:     0x55fa42a7e7c3 - std::sys_common::backtrace::__rust_begin_short_backtrace::hd032c6bd60d4bb43
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/sys_common/backtrace.rs:126
  23:     0x55fa42a73f0f - std::thread::Builder::spawn_unchecked::{{closure}}::{{closure}}::hb920c46fc29b2953
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/thread/mod.rs:470
  24:     0x55fa42a7e7a2 - <std::panic::AssertUnwindSafe<F> as core::ops::function::FnOnce<()>>::call_once::h168fffd117c6eacf
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/panic.rs:315
  25:     0x55fa42a7678a - std::panicking::try::do_call::h1076ad2f5efacc8f
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/panicking.rs:292
  26:     0x55fa42e3a7da - __rust_maybe_catch_panic
                               at src/libpanic_unwind/lib.rs:80
  27:     0x55fa42a76627 - std::panicking::try::hf1e36f1ebc83c33b
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/panicking.rs:271
  28:     0x55fa42a7ee04 - std::panic::catch_unwind::hcfc7851d11911124
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/panic.rs:394
  29:     0x55fa42a73d1a - std::thread::Builder::spawn_unchecked::{{closure}}::hefa782d3e7b7be16
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libstd/thread/mod.rs:469
  30:     0x55fa42a77464 - core::ops::function::FnOnce::call_once{{vtable.shim}}::h51e5352323a1df55
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/libcore/ops/function.rs:227
  31:     0x55fa42e26ccf - <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once::h483711add4ba2330
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/liballoc/boxed.rs:922
  32:     0x55fa42e39bd0 - <alloc::boxed::Box<F> as core::ops::function::FnOnce<A>>::call_once::h7605b45eb29ed0be
                               at /rustc/4560ea788cb760f0a34127156c78e2552949f734/src/liballoc/boxed.rs:922
  33:     0x55fa42e39bd0 - std::sys_common::thread::start_thread::h557b0c2dc4449562
                               at src/libstd/sys_common/thread.rs:13
  34:     0x55fa42e39bd0 - std::sys::unix::thread::Thread::new::thread_start::h7c2a7f9b68fe4bba
                               at src/libstd/sys/unix/thread.rs:79
  35:     0x7f6d6a487669 - start_thread
  36:     0x7f6d6a5c9323 - clone
  37:                0x0 - <unknown>

[2020-04-05T15:48:58Z INFO  actix_web::middleware::logger] 127.0.0.1:42894 "POST /f HTTP/1.1" 500 21 "-" "curl/7.58.0" 30.200412

But file actually saved,

ls /root/.local/share/filite/files
q8bo4a.vimrc

Question - Storing pastebin uploads as files

Can the uploads to the pastebin (in "Texts") be stored as files similar to how actual file uploads (in "Files") are handled?

Reason being that this way, the SQLite database need not be populated with the contents of the pastes, and instead store only the absolute path of the text files.

Option for different paths

For example, I think having /l/ for links is confusing since it could be seen as I or 1. Could this be an option in .env so I could make it /u/ (or something else) instead?

File link generation fails when serving on subdirectory.

I tried to serve filite on subdirectory, not on the root. My nginx configuration is:

location /u/ {
proxy_pass http://localhost:7500/;
}
location /u/f {
client_max_body_size 10M;
proxy_pass http://localhost:7500/;
}

Link and text works well, but file upload fails with alert window says only "Error".
Filite console log looks like
"PUT //76f2dy HTTP/1.0" 404 0 "https://luftaquila.io/u/"

I wonder that double slash in front of the id is just normal, or some bug caused by serving on subdirectory, or by any other problem.

Opt-in feature to set quota limits for file upload storage

In some scenarios, it would be useful to set the limit for the size of the directory storing the file uploads. Even better if it could be done via the config file. Example, when a user wants to not store more than 20GB of files on his server, he can simply set the config file as follows :

# Max allowed size for file uploads, in bytes
max_filesize = 10000000
# Max allowed size for file uploads directory, in gigabytes (set value to -1 for no limit)
max_filedir = 20

Filite would disable the file upload feature when the size of the directory reaches 20GB, and the admin would be notified in some way.

Add a reset command to generate a fresh config and/or password

When a user deletes the contents of the .filite/ folder, in order to start afresh, he can't generate a new config file.

The following error is displayed

$ ./filite.exe
Can't read config file.

When the .filite/ folder is deleted, the user can generate the config file again.

Requirement of glibc 2.18 breaks compatibility with older server distributions

Starting filite on a RedHat / CentOS 7 based server, the following error message occurs:

./filite: /lib64/libc.so.6: version `GLIBC_2.18' not found (required by ./filite)

Unfortunately there is no possibility in updating to CentOS 8 for me, as I am planning to use filite on a web server out of my control. I have all user rights, but the provider manages the machines.

It would be nice to have less restrictive requirements to support other distributions. CentOS 8 is not even a year old.

URL Shortening - display the link instead of opening the shortened link in a new tab

When a URL is shortened the existing behavior is that the shortened URL is opened in a new tab which them immediately resolves to the original URL. The purpose of a URL shortener is to shorten a url and being able to copy the url to share it to other people. But here, a user would have to remember the custom alias that he inputted, or the random alias that was generated before hitting the submit button.

UI bug in Chrome

All three sections of filite, i.e., "Links", "Files". and "Texts", have an additional grey bar at the bottom of the UI on Chrome. Also, the scroll bars, which are not visible on Firefox, are visible on Chrome.

0

1

The comparative screenshots between Chrome and Firefox is uploaded here

Allow custom host to bind to

This allows for filite to work in edge(?) cases where binding to localhost is not enough.
Binding to localhost or 127.0.0.1 allows the app to be accessible only by the host itself and with sandboxed network configuration this makes it impossible to have it accessible in certain setups.
I've made quick change to current source to allow it but I'm not good at Rust.
https://github.com/CatTheHacker/filite/commit/df89d52ae8fd513f4442d849e87bb7bfc1502fd6

Also in case of using reverse proxy in front of filite, it would be nice to have an option to use UNIX sockets to bypass network completely

diff --git a/src/main.rs b/src/main.rs
index 62cfd45..245d5e9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -95,7 +95,7 @@ async fn main() {
                     .route(web::delete().to(routes::texts::delete)),
             )
     })
-    .bind(&format!("{}:{}", host, port))
+    .bind_uds("/path/cat/.local/tmp/filite.sock")
     .unwrap_or_else(|e| {
         eprintln!("Can't bind webserver to specified port: {}", e);
         process::exit(1);

Nginx:

proxy_pass http://unix:/path/cat/.local/tmp/filite.sock:/

Set working directory

I would like to keep the rootfs of my server clean of anything except OS related files.
Could it be possible to supply a working directory as a command line option to filite?

This way, the config file and the files can be stored separately from the user under which filite is running.

Logout doesn't work

When I acces /logout I see the "Logged out" text, and I added a debug check to make sure identity.is_none() at that point, which it is... but then when I access any other page it's somehow remembered the identity. I tried this with Firefox and Gnome Web. I don't have any cookies stored:

[2019-10-26T12:19:49Z INFO  actix_web::middleware::logger] [::1]:34458 "GET /logout HTTP/1.1" 200 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15
(KHTML, like Gecko) Version/13.0 Safari/605.1.15 Epiphany/605.1.15" 0.000530

Go to '/'
**snip favico**

[2019-10-26T12:19:52Z INFO  actix_web::middleware::logger] [::1]:34458 "GET / HTTP/1.1" 200 13041 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/605.1.15 (K
HTML, like Gecko) Version/13.0 Safari/605.1.15 Epiphany/605.1.15" 0.000617
identity: filite

No password = no login prompt

When a user chooses to not set a password on the initial run (by leaving it blank), and wants filite to be accessible publicly, the login prompt should not be displayed while accessing filite.

Random URL generation doesn't work on mobile

This is due to the way the keyup event works. Could either be fixed by:

  • Editing the code to check the last character on input instead of checking the pressed key on keyup
  • Adding a UI element for random URL generation

Feature request: custom styling

It would be ideal if there is some sort of setting or config, where I can specify what style-sheet file the binary should use. I know the project uses Spectre-Dark and I modified the original stylesheet to be closer to gruvbox and I would like to use that, but I want to use the binary, rather than run the source code directly.

Using more than 6 letters results in "Error: Invalid ID"

Hello! If you try to use more than 6 letters in the links/text tab it results in "Error: Invalid ID", do this with files and it'll result in a "Bad gateway 502" error. URLs are somehow capped at 6 letters.

Config

database_url = "/home/cdn/.local/share/filite/database.db"
pool_size = 4
files_dir = "/home/cdn/.local/share/filite/files"

[highlight]
theme = "github"
languages = ["rust"]

Logs

(when doing the example below)

[2020-04-16T17:38:55Z INFO  actix_web::middleware::logger] 127.0.0.1:57806 "GET /f HTTP/1.0" 200 684 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" 0.000285
[2020-04-16T17:38:55Z INFO  actix_web::middleware::logger] [::1]:35722 "GET /l HTTP/1.0" 200 225 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" 0.000264
[2020-04-16T17:38:55Z INFO  actix_web::middleware::logger] 127.0.0.1:57810 "GET /t HTTP/1.0" 200 335 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" 0.000219
[2020-04-16T17:39:14Z INFO  actix_web::middleware::logger] [::1]:35726 "PUT /f/4578329478932743982 HTTP/1.0" 400 10 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" 0.000080
[2020-04-16T17:39:14Z INFO  actix_web::middleware::logger] 127.0.0.1:57814 "PUT /f/4578329478932743982 HTTP/1.0" 400 10 "https://example.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0" 0.000046

Example

1Xe2zOS

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.