Archive pour mars 2011

Serving static files with Apache while controlling access with Django

Dimanche 13 mars 2011

Theses days, I’ve added the ability to upload files in Aemanager for Ma Petite Auto Entreprise. Since a user may only access files he owns, a solution could have been putting them into an obfuscated directory (with a uid) in the media directory but I didn’t want to rely on such a solution which I consider not enough secure. I could have then let Apache serve theses files.

An other way to achieve this goal could be putting files in a directory not accessible by http, then in a view, read the file and stream the content with Django. It matches my goal but it isn’t efficient nor recommended to serve static files through Django.

The last solution, the better, is to use mod_xsendfile, a module for Apache. The user ask for a file represented by an arbitrary url and Django just sends a response without content, setting X-Sendfile header to tell Apache where it should read the content.

First, you need to install mod_xsendfile following these instructions :

$ sudo apxs2 -cia mod_xsendfile.c

Restart Apache when finished.

Here is a view of Aemanager which provide protected static files:

@settings_required
@subscription_required
def contract_uploaded_contract_download(request, id):
    contract = get_object_or_404(Contract, pk=id, owner=request.user)
    response = HttpResponse(mimetype='application/force-download')
    response['Content-Disposition'] = 'attachment;filename="%s"'\
                                    % smart_str(contract.contract_file.name)
    response["X-Sendfile"] = "%s%s" % (settings.FILE_UPLOAD_DIR, 
                                       contract.contract_file.name)
    response['Content-length'] = contract.contract_file.size

    return response

Here the important thing is the X-Sendfile header.

However, there’s also a problem with files which have non-ascii characters in their name. The best solution I’ve found is to use:

unicodedata.normalize('NFKD', filename).encode('ascii', 'ignore')

This replaces characters such as « é » by their ascii equivalent, « e » in this case.

Here is my model to let you see how it’s done :

store = FileSystemStorage(location=settings.FILE_UPLOAD_DIR)

def contract_upload_to_handler(instance, filename):
    return "%s/contract/%s" % (instance.owner.username,
                               unicodedata.normalize('NFKD',
                                                     filename).encode('ascii',
                                                                      'ignore'))

class Contract(OwnedObject):
    customer = models.ForeignKey(Contact,
                                 verbose_name=_('Customer'),
                                 related_name="contracts")
    title = models.CharField(max_length=255,
                             verbose_name=_('Title'))
    contract_file = models.FileField(upload_to=contract_upload_to_handler,
                                     null=True,
                                     blank=True,
                                     storage=store,
                                     verbose_name=_('Uploaded contract'))
    content = models.TextField(verbose_name=_('Content'),
                               null=True,
                               blank=True)
    update_date = models.DateField(verbose_name=_('Update date'),
                                   help_text=_('format: mm/dd/yyyy'))

EDIT : The Xsendfile module also exists for nginx and charset issue in filename can be solved with header X-Accel-Charset: utf-8

Copy a directory over ssh

Mardi 8 mars 2011

The useful command of the night since I’ve bought a new computer and I need to copy some data from the old one to the new one :

tar cj MY_DIR | ssh user@ip tar xj -C ~/

For 30 GB by wifi, it will be a bit more efficient than a sftp transfer.

Auto-entrepreneur et Paypal : quel chiffre d’affaire déclarer ?

Dimanche 6 mars 2011

Dans le cadre de Ma Petite Auto-Entreprise, je gère un abonnement Paypal à 11,88€ par an. Sur chaque transaction, Paypal prend une commission égale à 3,4% + 25 centimes, soit 65 centimes, prélevée à la source. Du coup, je touche réellement 11,23€. Pour autant, j’envoie une facture à chaque abonné portant sur un montant 11,88€.

Beaucoup d’auto-entrepreneurs comme moi se posent alors la question du chiffre à déclarer. Après avoir cherché sur google, je n’ai trouvé que des réponses dans des forums avec des avis divergents. Du coup, j’ai écrit à l’URSSAF pour en avoir le cœur net. J’ai reçu des réponses préformatées et il a fallu que je réitère ma question pour avoir une réponse à peine plus précise. Toujours est-il que la réponse semble bien être : on déclare ce qu’on facture. Donc il faut déclarer 11,88€ et on peut s’asseoir sur les 65 centimes de commissions même si on paie des charges dessus et qu’on ne les a jamais touchés.

J’avais une deuxième question à laquelle je n’ai pas eu de réponse : est-ce que le chiffre d’affaire payé via Paypal doit être compté à partir du moment où on le récupère sur notre compte ou alors dès qu’il est perçu sur Paypal ? D’après ce que j’ai lu sur des forums, je pense qu’il faut considérer Paypal comme un compte bancaire et donc déclarer les paiements dès qu’ils sont touchés sur Paypal.

Sortie officielle de Ma Petite Auto-Entreprise

Mardi 1 mars 2011

Ca y est, après plus d’une centaine de corrections depuis le lancement de la bêta, Ma Petite Auto-Entreprise est officiellement en ligne.

Logiciel en ligne de gestion pour auto-entrepreneurs

Pour rappel, Ma Petite Auto-Entreprise est un logiciel de gestion pour auto-entrepreneurs permettant de gérer ses clients, devis, factures, relances tout en suivant son chiffre d’affaire, son reste à faire, le plafond du statut et les cotisations à payer.

Bien mieux qu’un long discours, je vous invite à découvrir tout ça directement : http://www.mapetiteautoentreprise.fr/